洛谷 P5785 [SDOI2012] 任务安排
链接:
弱化版:P2365
题意:
有 \(n\) 个任务待完成,每个任务有一个完成时间 \(t_i\) 和费用系数 \(f_i\),相邻的任务可以被分成一批。从零时刻开始这些任务会被机器分批完成,在每批任务开始前机器有一个给定启动时间 \(s\),一批任务的完成时间是这批任务完成时间之和,同一批任务视作在同一时刻完成。
每个任务的费用是他的完成时刻和费用系数的乘积,请最小化总费用。
分析:
如果设 \(dp[i]\) 为第 \(i\) 个任务作为当前这一批任务的最后一个时的最优解,这样会很麻烦,因为会涉及到 "此时一共有多少批" 这个很麻烦的状态。这里有一个dp的trick:费用提前。
我们发现把第 \(i\) 个任务作为当前这一批任务的最后一个时,当前批内以及后面的任务都会多一个 \(s\cdot f_i\) 的费用,于是把 \(dp[i]\) 改为第 \(i\) 个任务作为当前这一批任务的最后一个时,加上后面任务的已有贡献的最优解。可以联系状态转移理解:
\]
于是设 \(sumf[i]=\sum\limits_{k=1}^if_k\),\(sumt[i]=\sum\limits_{k=1}^it_k\)
\]
于是
\]
依套路:
\]
于是求斜率为 \(sumt[i]\),过点 \((sumf[j],dp[j]-s\cdot sumf[j])\) 的直线的最小截距。
这里的 \(sumt[i]\) 并不是单调的,所以不能用单调队列优化,一个优秀的办法是二分找到第一个与下一个点之间的斜率大于 \(sumt[i]\) 的点,时间复杂度 \(O(n\log n)\)。
算法:
单调队列维护下凸包,然后每次二分找到最优决策点,根据其信息得到 \(dp[i]\),继续维护凸包即可。时间复杂度 \(O(n\log n)\)。
重要的细节:
这道题让我注意到了一个斜率优化dp中的一个很重要的细节,就是维护下凸包的判断。
这道题在洛谷讨论区也有不少人曾发起过讨论,称因为精度问题而必须把判断斜率改成乘法,但其实并不是这样一个简单的借口,我们来看下面三个代码,他们除了维护下凸包的判断没有任何区别。
(Y(q[qt])-Y(q[qt-1]))*(X(i)-X(q[qt])) >= (Y(i)-Y(q[qt]))*(X(q[qt])-X(q[qt-1])) 100pts
slope(q[qt],q[qt-1])>=slope(i,q[qt]) 80pts
slope(q[qt],q[qt-1])>=slope(i,q[qt-1]) 100pts
显然,这并不是精度的问题,甚至这三种写法都是错的!
我们来看下面这种情况:(这种情况虽然在数据上比较难构造,但单纯考虑维护下凸包来看还是很常见的)

这样三个横坐标相等点从上到下按 \(1,3,2\) 的次序被加入下凸包的维护。
显然,根据人脑判断,这样的数据要维护下凸包自然是保留 \(2\) 号点,弹出 \(3\) 号点,但普遍的写法是不存在 "弹出当前点" 这种操作的,只有 "弹出上一个点"。于是:
如果是第一种判断,两边的乘积都是 \(0\),于是会弹出 \(2\) 号点,此时 \(1,3\) 点间斜率为 -inf,后面的点可以加入,但在局部确实出现了正确性的错误。
如果是第二种判断,由于 \(1,2\) 间斜率是 -inf,\(2,3\) 点间斜率是 +inf,所以不会有点弹出,此时 \(2,3\) 点之间斜率为 +inf,这种情况下,后面的点根本无法加入下凸包的维护了,出现了严重的问题。
如果是第三种判断,由于 \(1,2\) 间斜率是 -inf,\(1,3\) 点间斜率是 -inf,这里也涉及取等的问题,如果取等,那么会弹出 \(2\) 号点,变成第一种情况;如果不取等,那么 \(2\) 号点不会被弹出,于是变成第二种情况。
所以这题错误的真正原因根本不是什么精度,而是维护下凸包的做法在存在横坐标相等的情况下本来就是假的!
根据这题横坐标非严格单调增,一个感觉比较正确的做法是在维护下凸包时先判断与前面的点横坐标是否相等,如果相等,保留纵坐标最低的点;如果不等,按照原本的做法即可。
在这样的做法下,不等号是否取等是没有正确性的影响的。取等会弹出一些无用的点,可能会快几毫秒。
所以在有横坐标相等的情况下,一个正确的写法是:
if(X(i)==X(q[qt])&&1<qt){if(Y(i)<Y(q[qt]))qt--;else continue;}
while(1<qt&&slope(q[qt],q[qt-1])>=slope(i,q[qt]))qt--;
q[++qt]=i;
这样写的话,不管判断是用的乘法还是斜率,哪两个斜率判断,或者取不取等,都是能过的。不排除真的会有精度错误,但我认为不能把根据观察发现除法比乘法劣的情况统一归咎到精度错误上。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-'0';c=getchar();}
return p*f;
}
#define X(x) (sumf[x])
#define Y(x) (dp[x]-sumf[x]*s)
#define dx(x,y) (X(x)-X(y))
#define dy(x,y) (Y(x)-Y(y))
#define slope(x,y) ((double)dy(x,y)/dx(x,y))
const int N=3e5+5;
const int inf=0x7fffffffffffffff;
int n,s,t[N],f[N],sumt[N],sumf[N],dp[N],q[N],qt;
inline int bin(int key){
int l=1,r=qt,mid;
while(l<r){
mid=(l+r)>>1;
double cp=(mid==qt)?inf:slope(q[mid+1],q[mid]);
if(cp<key)l=mid+1;
else r=mid;
}
return l;
}
signed main(){
n=in,s=in;
for(int i=1;i<=n;i++)
t[i]=in,f[i]=in,
sumt[i]=sumt[i-1]+t[i],
sumf[i]=sumf[i-1]+f[i];
qt=1;
for(int i=1;i<=n;i++){
int qh=bin(sumt[i]);
dp[i]=Y(q[qh])-sumt[i]*X(q[qh])+sumf[i]*sumt[i]+sumf[n]*s;
if(X(i)==X(q[qt])&&1<qt){if(Y(i)<Y(q[qt]))qt--;else continue;}
while(1<qt&&slope(q[qt],q[qt-1])>slope(i,q[qt-1]))qt--;
q[++qt]=i;
}
cout<<dp[n];
return 0;
}
题外话:
这个重要的细节确实让我对斜优里维护下凸包部分的印象深了许多。
洛谷 P5785 [SDOI2012] 任务安排的更多相关文章
- 洛谷P2751[USACO]工序安排
题目传送门 怎么说呢,这个题目我刚开始随便乱搞了几下,交了个暴力代码上去居然还水了49分,数据确实有点弱啊,然后看到洛谷上那位大佬Redbag的题解瞬间就佩服的五体投地,那真的是简洁.易懂又高效.直接 ...
- 洛谷P2365/5785 任务安排 题解 斜率优化DP
任务安排1(小数据):https://www.luogu.com.cn/problem/P2365 任务安排2(大数据):https://www.luogu.com.cn/problem/P5785 ...
- 洛谷 P2303 [SDOi2012]Longge的问题 解题报告
P2303 [SDOi2012]Longge的问题 题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数\(N\),你需要 ...
- 洛谷 P2498 [SDOI2012]拯救小云公主 解题报告
P2498 [SDOI2012]拯救小云公主 题目描述 英雄又即将踏上拯救公主的道路-- 这次的拯救目标是--爱和正义的小云公主. 英雄来到\(boss\)的洞穴门口,他一下子就懵了,因为面前不只是一 ...
- 洛谷P2303 [SDOi2012]Longge的问题
题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数N,你需要求出∑gcd(i, N)(1<=i <=N). ...
- 洛谷 P2837 晚餐队列安排
P2837 晚餐队列安排 题目背景 Usaco Feb08 Bronze 题目描述 为了避免餐厅过分拥挤,FJ要求奶牛们分2批就餐.每天晚饭前,奶牛们都会在餐厅前排队入内,按FJ的设想,所有第2批就餐 ...
- 洛谷P2303 [SDOi2012] Longge的问题 数论
看懂了题解,太妙了TT但是想解释的话可能要很多数学公式打起来太麻烦了TT所以我就先只放代码具体推演的过程我先写在纸上然后拍下来做成图片放上来算辣quq 好的那我先滚去做题了做完这题就把题解放上来.因为 ...
- 洛谷P2351 [SDOi2012]吊灯 【数学】
题目 Alice家里有一盏很大的吊灯.所谓吊灯,就是由很多个灯泡组成.只有一个灯泡是挂在天花板上的,剩下的灯泡都是挂在其他的灯泡上的.也就是说,整个吊灯实际上类似于[b]一棵树[/b].其中编号为 1 ...
- 洛谷P2498 [SDOI2012]拯救小云公主 【二分 + 并查集】
题目 英雄又即将踏上拯救公主的道路-- 这次的拯救目标是--爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1 ...
随机推荐
- RMI源码调试
看RMI漏洞时候,对其漏洞原理并不是很理解,所以简单调试了下源码加强下漏洞理解 由于要调试到RegistryImpl_Stub这种动态类,刚开始用的源码版本是JDK8u141,后来发现源码有些地方进行 ...
- Docker系列(22)- DockerFile指令说明并构建自己的centos
DockerFile常用指令 实战测试 DockerHub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建 #创建一个自己的centos # 1.编写D ...
- 启动docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
启动docker提示: docker: Got permission denied while trying to connect to the Docker daemon socket at uni ...
- redis 设置密码 laravel框架配置redis
* 参考资料 redis文档 http://www.redis.cn/documentation.html, http://redisdoc.com/index.html r ...
- ~/.emacs emacs 配置文件
windows ~/.emacs (when (>= emacs-major-version 24) (require 'package) (add-to-list 'package-archi ...
- centos7 .net core 使用supervisor守护进程后台运行
安装supervisor yum install supervisor 配置supervisor vi /etc/supervisord.conf 拉到最后,这里的意思是 /etc/superviso ...
- 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 百篇博客分析OpenHarmony源码 | v28.03
百篇博客系列篇.本篇为: v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...
- Orchard Core 配置项说明
Orchard Core使用IShellConfiguration扩展了ASP.NET Core IConfiguration,以允许在应用程序范围的配置之上进行特定于租户的配置.虽然本文档使用Orc ...
- P5488-差分与前缀和【NTT,生成函数】
正题 题目链接:https://www.luogu.com.cn/problem/P5488 题目大意 求一个长度为$n$的序列的$k$阶差分/前缀和. 解题思路 先考虑前缀和怎么做 搞出来生成函数就 ...
- 国庆出游神器:魔幻黑科技换天造物,让vlog秒变科幻大片!
摘要:国庆旅游景点人太多,拍出来的照片全是人人人.车车车,该怎么办?不妨试试这个黑科技,让你的出游vlog秒变科幻大片. 本文分享自华为云社区<国庆出游神器,魔幻黑科技换天造物,让vlog秒变科 ...