<更新提示>

<第一次更新>摆渡车的题解我已经写过一遍了,在这里,这次主要从斜率优化的角度讲一下摆渡车,并总结一下斜率优化会出现的一些奇奇怪怪的错误。


<正文>

摆渡车

Description

有 n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i 位同学在第 titi分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

注意:摆渡车回到人大附中后可以即刻出发。

Input Format

第一行包含两个正整数 n,m,以一个空格分开,分别代表等车人数和摆渡车往返 一趟的时间。

第二行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i 个非负整数 ti代 表第 i 个同学到达车站的时刻。

Output Format

输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。

Sample Input

5 5
11 13 1 5 5

Sample Output

4

解析

如果考虑斜率优化的思路的话,我们可以这样设置状态:\(f_i\)代表到了时间点\(i\),所有同学等待时间的最小和。那么就可以这样写状态转移方程:

\[f_i=min_{j\leq i-m}\{f_j+\sum_{j<t_k\leq i}(i-t_k)\}
\]

然后可以用前缀和把求和式拆一下,设\(cnt_i\)代表到时间点\(i\)位置已经到达过车站的学生个数,\(sum_i\)代表已经到达过车站的学生的到达时间总和,则原方程可以写为:

\[f_i=min_{j\leq i-m}\{f_j+(cnt_i-cnt_j)*i-(sum_i-sum_j)\}
\]

好了,看到有\(i,j\)乘积项就可以考虑斜率优化了,我们再整理一下式子:

\[f_i=min_{j\leq i-m}\{f_j+sum_j-cnt_j*i\}+cnt_i*i-sum_i
\]

假设找到了最优的决策点\(j\),那么就有:

\[f_i=f_j+sum_j-cnt_j*i+cnt_i*i-sum_i\\f_j+sum_j=cnt_j*i+f_i-cnt_i*i+sum_i
\]

把\(f_j+sum_j\)看做\(y\),把\(cnt_j\)看做\(x\),把\(i\)看作\(k\),把\(f_i-cnt_i*i+sum_i\)看做\(b\),这就是直线方程了,然后直接维护下凸壳,利用决策单调性转移就可以了。

但其实这道题还没那么简单,其实还有如下几个问题:

\(1.\) 变量\(j\)有取值范围,需满足\(j\leq i-m\)。

\(2.\) 计算斜率可能出现分母为\(0\)。

\(3.\) \(f\)数组缺少部分初值。

对于第一个问题,我们采用分层推入队列的方法:在维护单调队列时,每当完成了对\(f_i\)的转移,我们应尝试把\(i-m+1\)这一个决策点推入队列,这样就能保证满足决策变量\(j\)的取值范围合法,其他题也是一样的。

对于第二个问题,计算斜率时分母为\(0\)就对应了坐标系内两个点坐标横坐标相同,但是纵坐标不同的情况。相应地,我们将这种情况视为两点之间的斜率为\(+\infty\)或\(-\infty\)(按照两点位置判断)处理即可,这样处理既简单,又对正确性没有影响。

第三个问题就对应了第一个问题,对于前\(m\)的点,显然我们是没有初值的,也无法用单调队列转移,所以我们必须要暴力对前\(m\)个状态进行更新,才能进行斜率优化,这个就根据\(dp\)式来更新就可以了。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const long long N=1e6+20,M=1e6+20,T=9e6+20,INF=1e18;
long long n,m,maxT,c[T],s[T],f[T];
long long q[T],head,tail,ans=INF;
inline void input(void)
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++)
{
long long t;scanf("%lld",&t);
maxT = max(t,maxT);
s[t] += t; c[t] ++;
}
}
inline void init(void)
{
for (int i=1;i<=maxT+m;i++)
c[i] += c[i-1] , s[i] += s[i-1];
}
inline double slope(int x,int y)
{
if ( c[x] == c[y] ) return f[y]+s[y] > f[x]+s[x] ? INF * 1.0 : INF * -1.0;
return ( 1.0 * (f[y]+s[y]) - 1.0 * (f[x]+s[x]) ) / ( 1.0 * (c[y]) - 1.0 * (c[x]) );
}
//f[i] = min{f[j]+(c[i]-c[j])*i-(s[i]-s[j])}
inline void dp(void)
{
head = tail = 1; q[tail] = 0;
for (int i=1;i<m;i++)
f[i] = c[i] * i - s[i];
for (int i=m;i<=maxT+m;i++)
{
while ( head<tail && slope(q[head],q[head+1]) <= 1.0 * i ) head++;
f[i] = f[q[head]] + (c[i]-c[q[head]]) * i - (s[i]-s[q[head]]);
while ( head<tail && slope(q[tail-1],q[tail]) >= slope(q[tail],i-m+1) ) tail--;
q[++tail] = i-m+1;
if (i>=maxT) ans = min(ans,f[i]);
}
}
int main(void)
{
input();
init();
dp();
printf("%lld\n",ans);
return 0;
}

总结

其实本题中的这一些问题就对应了斜率优化题中可能会出现的种种问题,以下我们对斜率优化总结一下:

解题基本思路:

\(1.\) 写出状态转移方程,看看能不能用斜率优化

\(2.\) 如果可以斜率优化,将方程改写为直线方程的形式,先用数形结合法尝试做一下

\(3.\) 如果可行,尝试证明一下决策单调性,用代数法推一推

\(4.\) 看看是什么类型的斜率优化:取\(min\)就维护下凸壳,取\(max\)就维护上凸壳

\(5.\) 注意一下\(i,j\)乘积项的符号,如果是负数一般把负号看成是与\(j\)有关项的而非斜率项的,然后在坐标系内重新画一下图,看看维护的到底是什么

\(6.\)最后看单调性会不会出问题,如果决策单调性出问题就用二分答案,如果凸壳不能单调维护就用\(cdq\)分治

常见问题:

\(1.\) 决策变量有取值范围,在推入队列的时候改为推入可取的决策变量

\(2.\) \(dp\)数组有部分初值无法用斜率优化转移得到(决策变量被取值范围限制),暴力先转移初值

\(3.\) 斜率会出现整数被\(0\)除,特判返回\(+\infty\)或\(-\infty\)

\(4.\) 计算斜率可能会出锅,\(slope\)函数尽量公式化:数值大的下标为\(y\),数值小的下标为\(x\),计算时用\(val(y)-val(x)\)

\(5.\) 单调队列要注意:必须在队列内有至少两个元素才能删除队首或队尾

\(6.\) 浮点数运算很容易出问题,计算斜率或比较大小时记得转为\(double\)类型

\(7.\) 精度可能会出问题,适当时计算斜率的除法要转为乘法

\(8.\) 考虑单调队列内是否要存一个转移初值(如\(0\))

\(9.\) \(dp\)数组的初值:\(+\infty\)或\(-\infty\)或\(0\),是否要\(long\ long\),无穷要开够大

\(10.\) 弹出队首不优元素和队尾不在下凸壳内元素比较斜率时,请将等号加上(\(<\)尽量写成\(\leq\),\(>\)尽量写成\(\geq\))

\(11.\) 写二分,请按模板来:\(while\)循环写\(l<r\),每一次计算\(slope(q[mid],q[mid+1])\),若小于等于斜率关键值,则\(l=mid+1\),反之\(r=mid\),最后返回\(q[l]\)

\(12.\)写\(cdq\)分治,也请按模板来:记得在递归子问题前先左右按编号大小分一下,避免出现\(f_i\)转移到\(f_j\)但是\(j>i\)的情况

\(cdq\)分治和二分的模板见『任务安排 斜率优化及其变形』,另外一片斜率优化数形结合入门博客见『玩具装箱TOY 斜率优化DP』,简单总结博客见『土地征用 Land Acquisition 斜率优化DP』


<后记>

『摆渡车 斜率优化dp及总结』的更多相关文章

  1. 『土地征用 Land Acquisition 斜率优化DP』

    斜率优化DP的综合运用,对斜率优化的新理解. 详细介绍见『玩具装箱TOY 斜率优化DP』 土地征用 Land Acquisition(USACO08MAR) Description Farmer Jo ...

  2. 【学习笔记】动态规划—斜率优化DP(超详细)

    [学习笔记]动态规划-斜率优化DP(超详细) [前言] 第一次写这么长的文章. 写完后感觉对斜优的理解又加深了一些. 斜优通常与决策单调性同时出现.可以说决策单调性是斜率优化的前提. 斜率优化 \(D ...

  3. bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)

    题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地 ...

  4. bzoj-1096 1096: [ZJOI2007]仓库建设(斜率优化dp)

    题目链接: 1096: [ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L ...

  5. [BZOJ3156]防御准备(斜率优化DP)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3156 分析: 简单的斜率优化DP

  6. 【BZOJ-1096】仓库建设 斜率优化DP

    1096: [ZJOI2007]仓库建设 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3719  Solved: 1633[Submit][Stat ...

  7. BZOJ 1010: [HNOI2008]玩具装箱toy 斜率优化DP

    1010: [HNOI2008]玩具装箱toy Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再 ...

  8. BZOJ 3156: 防御准备 斜率优化DP

    3156: 防御准备 Description   Input 第一行为一个整数N表示战线的总长度. 第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai. Output 共一个整数,表示最小的战 ...

  9. HDU2829 Lawrence(斜率优化dp)

    学了模板题之后上网搜下斜率优化dp的题目,然后就看到这道题,知道是斜率dp之后有思路就可以自己做不出来,要是不事先知道的话那就说不定了. 题意:给你n个数,一开始n个数相邻的数之间是被东西连着的,对于 ...

随机推荐

  1. WampServer出现You don’t have permission to access/on this server提示

    WampServer出现You don’t have permission to access/on this server提示 本地搭建WampServer,输入http://127.0.0.1访问 ...

  2. Android 自定义ListView动态加载数据

    我们都知道网络取数据是耗时操作,如果我们一次性请求所有数据,假如数据量不多那还可以接受,但是如果数据量特别多,那么带来的后果就是用户的愤怒(用户是很没有耐心的),所以这时候我们就需要动态的加载数据,分 ...

  3. Class.forName() 与 ClassLoader.loadClass()的区别

        看到一个面试题,说说Class.forName() 与 ClassLoader.loadClass()的区别,特意记录一下,方便后续查阅.     在我们写java代码时,通常用这两种方式来动 ...

  4. 【前端_React】npm常用命令

    安装模块(包): //全局安装 $ npm install 模块名 -g //本地安装 $ npm install 模块名 //一次性安装多个 $ npm install 模块1 模块2 模块n -- ...

  5. 部署LNMP应用平台

    一.LNMP应用平台概述 1.概述:LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构.Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/ ...

  6. Mysql数据库之备份还原(mysqldump,LVM快照,select备份,xtrabackup)

    备份类型: 热备份:读写不受影响 温备份:仅可执行读备份 冷备份:离线备份,读写均不能执行,关机备份 物理备份和逻辑备份 物理备份:复制数据文件,速度快. 逻辑备份:将数据导出之文本文件中,必要时候, ...

  7. [转]【response】HttpServletResponse接口

    创建时间:6.19 & 6.24 1.HttpServletResponse概述 我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有两个参 ...

  8. 性能测试基础---ant集成1

    ·Jmeter的命令行与ant等的集成.·为什么需要使用Jmeter的命令行模式(Non-GUI).·为了更好的利用负载机的资源.GUI模式会消耗更多的系统资源.·为了更好的掌握jmeter和其它工具 ...

  9. 用JSON.parse(JSON.stringify(itemData))序列化反序列化实现‘深度复制’

    还可以用来去除值不具有JSON 表示形式(数字.字符串.逻辑值.数组.对象.null)的属性,也就是说像undefined和function这样的属性值.

  10. Andriod Studio安装及使用

    创建Andriod项目 1.下载最新版的Andriod studio 2.在 Welcome to Android Studio 窗口中,点击 Start a new Android Studio p ...