[P5785 [SDOI2012]任务安排] 题解-斜率优化
P5785 [SDOI2012]任务安排
分析
很明显是一个dp
我们不妨设
由于每一批任务前都要一个时间为
我们不妨把每一个这样的
我们再用
那么
设
则
这就是一个可以用斜率优化的dp方程式了
斜率优化
- 设两个决策点
, 优于
注意此时 ,那么在后续的化简中,遇到负数要变号
- 拆开不等式
注意这里的 ,所以下一步需要变号
还可以再把 代入进行化简
斜率就这样推出来啦!
注意题目中
所以我们就不能随意把队首元素删除,而在找队首时只能用二分
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e5+10;
int n,head,tail;
ll s,T[maxn],t[maxn],c[maxn],sumc[maxn];
ll G[maxn],dp[maxn];
int dq[maxn*2];
int find(ll x){
if(head==tail) return head;
int l=head,r=tail;
while(l<r){
int mid=(l+r)>>1;
if(dp[dq[mid+1]]-dp[dq[mid]]<=(x+s)*(sumc[dq[mid+1]]-sumc[dq[mid]])) l=mid+1;
else r=mid;
}
return l;
}
int main(){
/*2023.5.1 hewanying P5785 [SDOI2012]任务安排 斜率优化dp*/
scanf("%d%lld",&n,&s);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&T[i],&c[i]);
sumc[i]=sumc[i-1]+c[i];
t[i]=t[i-1]+T[i];
G[i]=t[i]*sumc[i];
}
memset(dp,0x3f,sizeof(dp));dp[0]=0;
for(int i=1;i<=n;i++){
int op=find(t[i]);
dp[i]=dp[dq[op]]+s*(sumc[n]-sumc[dq[op]])-t[i]*sumc[dq[op]]+G[i];
while(head<tail&&(dp[dq[tail]]-dp[dq[tail-1]])*(sumc[i]-sumc[dq[tail]])>=(dp[i]-dp[dq[tail]])*(sumc[dq[tail]]-sumc[dq[tail-1]]))
tail--; //这个地方必须要取>=在=时不用压入队中,否则会影响二分的进行,二分涉及相等的问题
dq[++tail]=i;
}
printf("%lld\n",dp[n]);
return 0;
}
总结
- 斜率优化还是存在精度问题,这道题就有两斜率相等的情况,所以直接变除为乘
- 在
递增时才能用双端队列直接把队首删去,而这道题数组中存在负数,就只有用二分来计算 - 在dp开始前,不用加
,因为初始化中 ,这样一来dq中就有两个0了 - 在删除队尾时,两斜率相等也是不满足条件的,所以要用
如果等于没有排除,会影响二分的进行
本文作者:H_W_Y
本文链接:https://www.cnblogs.com/H-W-Y/p/17366211.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步