XII.任务安排
斜率优化真\(\color{black}\colorbox{black}{XX}\)有意思!!
设\(t[i]\)表示原题中的\(t_i\)的前缀和,\(c[i]\)表示原题中\(f_i\)的前缀和,\(m\)表示启动时间\(s\)。
思路1:\(n^3\)DP:
设\(f[i][j]\)表示:前\(i\)个位置,分成\(j\)组,的最快时间。
则有\(f[i][j]=\min\limits_{k=0}^{i-1}\{f[k][j-1]+(t[i]+m*j)*(c[i]-c[k])\}\)。
思路2:\(n^2\)DP:
观察到我们每在第\(i\)个点前多分出一组,则在\(i\)后面所有东西的时间都会向后拖\(m\)时刻,共计造成\(m*(c[n]-c[i])\)点费用,
因此我们可以设\(f[i]\)表示前\(i\)个位置的最短时间,
则有\(f[i]=\min\limits_{j=0}^{i-1}\{f[j]+m*(c[n]-c[j])+t[i]*(c[i]-c[j])\}\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,f[10010],t[10010],c[10010];
int main(){
scanf("%d%d",&n,&m),memset(f,0x3f3f3f3f,sizeof(f)),f[0]=0;
for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&c[i]),t[i]+=t[i-1],c[i]+=c[i-1];
for(int i=1;i<=n;i++)for(int j=0;j<i;j++)f[i]=min(f[i],f[j]+m*(c[n]-c[j])+t[i]*(c[i]-c[j]));
printf("%d\n",f[n]);
return 0;
}
思路3:考虑斜率优化。
设\(j<k<i\),且\(j\)比\(k\)更优。
则有
\(f[j]+m*(c[n]-c[j])+t[i]*(c[i]-c[j])<f[k]+m*(c[n]-c[k])+t[i]*(c[i]-c[k])\)
拆括号
\(f[j]+m*c[n]-m*c[j]+t[i]*c[i]-t[i]*c[j]<f[k]+m*c[n]-m*c[k]+t[i]*c[i]-t[i]*c[k]\)
消元
\(f[j]-m*c[j]-t[i]*c[j]<f[k]-m*c[k]-t[i]*c[k]\)
移项并合并
\(f[j]-f[k]-(m+t[i])*(c[j]-c[k])<0\)
再移
\(f[j]-f[k]<(m+t[i])*(c[j]-c[k])\)
注意因为\(j<k\),且\(c\)是前缀和,则\(c[j]<c[k]\)。不等式两边除以负数,应该换方向。
最终得到
\(\dfrac{f[j]-f[k]}{c[j]-c[k]}>m+t[i]\)
左边的东西仅与\(j\)和\(k\)有关;右边的东西是单调的(\(t\)也是前缀和);
因此就可以斜率优化辣。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,t[10100],c[10100],f[10100],q[10100],l,r;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&c[i]),t[i]+=t[i-1],c[i]+=c[i-1];
for(int i=1;i<=n;i++){
while(r-l&&(f[q[l]]-f[q[l+1]])>=(c[q[l]]-c[q[l+1]])*(m+t[i]))l++;
f[i]=f[q[l]]+m*(c[n]-c[q[l]])+t[i]*(c[i]-c[q[l]]);
while(r-l&&(f[q[r-1]]-f[q[r]])*(c[q[r]]-c[i])>=(f[q[r]]-f[i])*(c[q[r-1]]-c[q[r]]))r--;
q[++r]=i;
}
printf("%d\n",f[n]);
return 0;
}