1 条题解
-
0
自动搬运
来自洛谷,原作者为

ButterflyDew
life is hard搬运于
2025-08-24 21:36:50,当前版本为作者最后更新于2018-07-16 20:02:46,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
其他题解里面的斜率优化讲的对新手不友好,我详细说一下吧
先考虑的做法
因为分的批数是不定的,所有我们为了避免算后面的时候要用到前面分了多少批这个状态,我们采用费用提前的思想,在处理前面时把产生的费用给计算了。
方程:代表前个任务分成若干批产生的最小费用
转移:$f[i]=min_{i=0}^{i-1}(f[j]+t[i]*(c[i]-c[j])+S*(c[n]-c[j]))$ 其中,分别是任务时间和费用的前缀和的数组
考虑将状态转移方程化简: 注意:我们这里把去掉了,是把的取值集合所映射的和分别作为函数的和
那么这个一次函数的斜率就等于,而截距等于
我们想让这个最小,那么其实就等价于最小,在坐标系中,如果我们拿一个已知斜率的直线向上滑动,当它第一次碰见取值集合内的点时,就取到了它的最小值
1.考虑什么时候一个点可以取到最小值
通过手玩我们发现(对于这个手玩真的是最好的理解方式了)
对于点,当以为关键字排序后的两个相邻的点和(在此题中对应的单调性)
当斜率时,此点就是最优转移点
2.考虑什么时候一个点可能可以取到最小值,什么时候一定不能
如下图,当斜率时,是有机会的,此时三个点下凸
当斜率时,不会有一点机会,此时三个点上凸
此时我们就可以用单调队列维护一个点集了,其中相邻点的斜率必定是递增的
当需要做出决策的时候,我们二分这个点集,找到最优转移点。
针对于此题,我们发现每个决策点的斜率是单调递增的,那么我们可以直接维护队首,保证每次从队首转移。即当当前斜率大于队首与第二个点之间的斜率时,就出队。
统计完答案后再用二元组更新队尾的元素,然后将它放进去
Code:
#include <cstdio> #include <cstring> const int N=5010; int f[N],t[N],c[N],n,S,q[N],l,r; int main() { scanf("%d%d",&n,&S); for(int i=1;i<=n;i++) { scanf("%d%d",t+i,c+i); t[i]+=t[i-1]; c[i]+=c[i-1]; } memset(f,0x3f,sizeof(f)); f[0]=0; l=1,r=1;//注意此时已经把0作为元素放入了q[1]中去了 for(int i=1;i<=n;i++) { while(l<r&&f[q[l+1]]-f[q[l]]<=(S+t[i])*(c[q[l+1]]-c[q[l]])) l++; f[i]=f[q[l]]+t[i]*c[i]+S*c[n]-c[q[l]]*(S+t[i]); while(l<r&&(f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]])<=(f[q[r]]-f[q[r-1]])*(c[i]-c[q[r]])) r--; q[++r]=i; } printf("%d\n",f[n]); return 0; }
- 1
信息
- ID
- 1372
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者