任务安排

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;
}

原文地址:https://www.cnblogs.com/Troverld/p/14596856.html