1. 程式人生 > >BZOJ2726:任務安排(DP+斜率優化+二分)

BZOJ2726:任務安排(DP+斜率優化+二分)

printf 序列 -c int ret 復雜 不能 clu 整數

機器上有N個需要處理的任務,它們構成了一個序列。這些任務被標號為1到N,因此序列的排列為1,2,3...N。這N個任務被分成若幹批,每批包含相鄰的若幹任務。從時刻0開始,這些任務被分批加工,第i個任務單獨完成所需的時間是Ti。在每批任務開始前,機器需要啟動時間S,而完成這批任務所需的時間是各個任務需要時間的總和。註意,同一批任務將在同一時刻完成。每個任務的費用是它的完成時刻乘以一個費用系數Fi。請確定一個分組方案,使得總費用最小。 Input 第一行兩個整數,N,S。 接下來N行每行兩個整數,Ti,Fi。 Output 一個整數,為所求的答案。 Sample Input 5 1 1 3 3 2 4 3 2 3 1 4

Sample Output

153

思路:註意是只有一臺機器,所以時間是累加的,那麽影響到[j,N]。所以列出方程: f[i]=min(): f[j]+(sum2[N]-sum2[j])*(sum1[i]-sum1[j]+M)

直接dp復雜度是O(N^2),使用效率優化:

//b=y-kx+c; --> f[i]=(-sum1[i]*sum2[j])+(f[j]+sum1[j]*sum2[j]-sum*sum1[j]-M*sum2[j])+(M*sum+sum*sum1[j]);
其中k之和i有關,y之和j有關,b就是f[i],c是常數:k=sum1[i],y=f[j]-sum2[N]*sum1[j]+sum2[j]*sum1[j]-sum2[j]*M;

可以看到我們需要維護一個斜率上升的凸包,由於K=sum1[i]沒有說遞增,所以我們不能彈出棧頂,求的時候用二分求得凸包極值。

註意:1,二分的時候,二分區間[0,top],0代表的是,從頭到尾都選,不能忽略。

2,每個新的i都要插入,插入當前i時,要維護斜率遞增。

3,維護的圖像的x和y分別的 y=kx+b的x和y,所以維護斜率,彈出棧尾比較斜率時,x是sum2[q[top]],而不是q[top];

//b=y-kx+c; --> f[i]=(-sum1[i]*sum2[j])+(f[j]+sum1[j]*sum2[j]-sum*sum1[j]-M*sum2[j])+(M*sum+sum*sum1[j]);
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1000010; ll sum1[maxn],sum2[maxn],f[maxn],M; int q[maxn],top,N; ll Y(int j){ return f[j]-sum2[N]*sum1[j]+sum2[j]*sum1[j]-sum2[j]*M; } ll getans(int i,int j){return f[j]+(sum2[N]-sum2[j])*(sum1[i]-sum1[j]+M);; } int main() { int i; scanf("%d%lld",&N,&M); for(i=1;i<=N;i++){ scanf("%lld%lld",&sum1[i],&sum2[i]); sum1[i]+=sum1[i-1]; sum2[i]+=sum2[i-1]; } for(int i=1;i<=N;i++){ int L=0,R=top,ans=top; while(L<=R){ int Mid=(L+R)>>1; if(getans(i,q[Mid])<=getans(i,q[Mid+1])) R=Mid-1,ans=Mid; else L=Mid+1; } f[i]=getans(i,q[ans]); while (top>0&& 1ll*(Y(q[top])-Y(q[top-1]))*(sum2[i]-sum2[q[top]])>=(Y(i)-Y(q[top]))*(sum2[q[top]]-sum2[q[top-1]]))
top--; q[++top]=i; //while語句裏不是dx的時候不是下邊之間減,是方程組的sum2來減。 } printf("%lld\n",f[N]); return 0; }

BZOJ2726:任務安排(DP+斜率優化+二分)