<更新提示>

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


<正文>

摆渡车

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. 设计模式之代理模式(proxy pattern)

    代理模式的本质是一个中间件,主要目的是解耦合服务提供者和使用者.使用者通过代理间接的访问服务提供者,便于后者的封装和控制.是一种结构性模式. 1.目的 为外部调用者提供一个访问服务提供者的代理对象. ...

  2. 蓝色映象 幻舞少女之剑 BLUE REFLECTION 后感

    到底是看片收获多还是游戏收获多?在刷蓝色反射的时候刷了2部番.所以,我到底是为了什么在玩游戏呢? 岸田メル的人设,毋庸置疑,唯美想舔,且总能给人一种绝无杂质,纯洁治愈的感觉,再加上浅野隼人的配乐,恰如 ...

  3. HTTP协议那些事儿

    HTTP协议简介 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP是万维网的数据通信的基础. ...

  4. Nginx 核心配置-新建一个web站点

    Nginx 核心配置-新建一个web站点 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Nginx基础配置常用参数说明 [root@node101.yinzhengjie.or ...

  5. 两数相加[链表加法] LeetCode.2

    给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...

  6. selenium+python关于页面滚动条滑动到底的问题总结

    1.如果滚动条是针对整个HTML可以用如下方式: js = "var q=document.documentElement.scrollTop=10000" # documentE ...

  7. Navicat Premium 12 永久使用办法

    1.按步骤安装Navicat Premium,如果没有可以去官网下载:http://www.navicat.com.cn/download/navicat-premium 2.安装好后下载激活文件:h ...

  8. 什么是amp?amp有什么用处?

    AMP是移动页面加速器Accelerated Mobile Pages的简称,是Google带领开发的开源项目,目的是为提升移动设备对网站的访问速度.它的核心称作AMP HTML,是一种新型的HTML ...

  9. AtCoder Grand Contest 038题解

    好久没更了 写点东西吧= = A 01Matrix 简单构造 左上角和右下角染成1其他染成0即可 #include<bits/stdc++.h> #define ll long long ...

  10. eclipse export runnable jar(导出可执行jar包) runnable jar可以执行的

    如果要导出可运行的JAR文件,需要选择Runnable Jar File. 1. 选择要到处JAR文件的工程,右键选择“Export”: 2. 选择“Java-->Runnable JAR fi ...