题意:一台机器要按照顺序完成n个任务,每个任务都有一个代价f和需要时间t。机器完成任务的方式是分批处理,对于每一批任务需要首先预处理s时间,同批任务中所有单个任务都是同时完成,代价为完成的时刻乘以各自的代价。求最小代价。
题解:类似于四边形不等式的dp。
1、分批考虑情况太多,可以先将问题转化。每个任务对对最后代价的贡献实际上等于它及它以后的f之和乘以它的时间t,即后面的任务都要为它等上t的时间,会多花f*t的代价。
2、找i的决策点的方法即min(dp[p]+(st[i]-st[p]+s)*sf[i]),st[i]为>=i的时间总和,sf[i]为大于等于i的代价总和,若两个决策点,j,k(j<k),存在j不比k更差,则意味着:
dp[j]+(st[i]-st[j]+s)*sf[i]<=dp[k]+(st[i]-st[k]+s)*sf[i] ==> (dp[j]-dp[k])/(st[j]-st[k])<=sf[i]
我们记用g(j,k)为用决策点j去取代k的代价函数,即考虑第i个点的时候如果发生了g(j,k)<=sf[i],那么用k就不如用j。
3、考虑[i,j]的决策点p,如果新加入一个点i-1,p是不会向后移的,例如,假设新决策点变成了k,由2的过程可以看出,只有g(p,k)<=sf[i]时p才能成为决策点,而新加入一个点后,sf[i]变成了sf[i-1],比原来更大了。于是完成第一步优化即,只能在p前面找新的决策点,但是这样也是n^2的,会超时。
4、将可能的决策点组成一个队列,第2步实际上是将所有p以前,i以后的点都加进队列里了,实际上是不需要的。当进行到i点时,可以通过先前的决策队列确定dp[i],然后需要加入新的决策点i,考虑此时决策队列的末尾两元素j,k,如果g(i,j)<=g(j,k),那么就可以将j从决策队列中删除了。例如,对x进行决策时,如果g(j,k)<=sf[x],由于g(i,j)<=g(j,k),就变成了用决策点j替换k,然后又用决策点i替换j了,j实际上没有什么作用,反之,如果g(j,k)>sf[x],它没可能,但g(i,j)比他小,更有可能得到新决策点。
5、由4便可以知道,只需维护一个决策点序列,一个决策点至多进队出队一次,于是便化为O(n)的解法了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int dp[12000],st[12000],sf[12000],L[12000]; 6 int main() 7 { 8 int n,s; 9 while(scanf("%d%d",&n,&s)!=EOF) 10 { 11 for(int i=1;i<=n;i++) 12 scanf("%d%d",st+i,sf+i); 13 dp[n+1]=st[n+1]=sf[n+1]=0; 14 for(int i=n;i>=1;i--) 15 st[i]+=st[i+1],sf[i]+=sf[i+1]; 16 int a=0,b=0; 17 L[a]=n+1; 18 for(int i=n;i>=1;i--) 19 { 20 while(a<b&&dp[L[a+1]]-dp[L[a]]<=(st[L[a+1]]-st[L[a]])*sf[i]) 21 a++; 22 dp[i]=dp[L[a]]+(st[i]-st[L[a]]+s)*sf[i]; 23 while(a<b&&(dp[i]-dp[L[b]])*(st[L[b]]-st[L[b-1]])<=(st[i]-st[L[b]])*(dp[L[b]]-dp[L[b-1]])) 24 b--; 25 L[++b]=i; 26 } 27 printf("%d\n",dp[1]); 28 } 29 return 0; 30 }