题意:一台机器要按照顺序完成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)的解法了。

View Code
 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 }