首页 > 代码库 > 洛谷P2365 任务安排 [解法二 斜率优化]

洛谷P2365 任务安排 [解法二 斜率优化]

解法一:http://www.cnblogs.com/SilverNebula/p/5926253.html

 

解法二:斜率优化

在解法一中有这样的方程:dp[i]=min(dp[i],dp[j]+(sumf[i]-sumf[j])*sumt[i]+s*(sumf[n]-sumf[j]) )

其中min的后半部分,也就是dp[j]+(sumf[i]-sumf[j])*sumt[i]+s*(sumf[n]-sumf[j]) 计算了将j~i分为一组的花费(以及提前计算的受影响花费)

设f(j)=dp[j]+(sumf[i]-sumf[j])*sumt[i]+s*(sumf[n]-sumf[j]),i不变时,若 f(j1)<f(j2) ,显然从j1到i分为一组比j2到i分为一组的答案更优,而如果j1<j2,显然j2可以被舍弃掉。由以上两个限制条件很容易联想到单调队列,进而想到斜率优化(并不)。

现在来考虑 j1<j2 ,f(j1)<f(j2) 的情况。把f()展开写再化简,可以得到(dp[j1]-dp[j2])/(sumf[j1]-sumf[j2])<=sumt[i]+s    (sumf和sumt分别是f、t的前缀和)

利用这个式子列斜率方程,维护一个下凸壳即可←然而并不能理解

我的想法:(dp[j1]-dp[j2])/(sumf[j1]-sumf[j2])显然是越小越好,我们可以据此维护斜率单调队列的队尾(具体看代码),而上面那个式子用来维护队头,即可行:

斜率优化10ms,O(n^2)算法43ms

 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<queue> 6 #include<cstring> 7 using namespace std; 8 const int mxn=6000; 9 int n;10 int s;11 int t[mxn],f[mxn];12 int sumt[mxn],sumf[mxn];13 int dp[mxn];14 int q[mxn];15 int gup(int j,int k){16     return (dp[j]-dp[k]);17 }18 int gdown(int j,int k){19     return sumf[j]-sumf[k];20 }21 int gdp(int i,int j){22     return dp[j]+(sumf[i]-sumf[j])*sumt[i]+s*(sumf[n]-sumf[j]);23 }24 int main(){25     scanf("%d%d",&n,&s);26     int i,j;27     for(i=1;i<=n;i++){28         scanf("%d%d",&t[i],&f[i]);29         sumt[i]=sumt[i-1]+t[i];30         sumf[i]=sumf[i-1]+f[i];31     }32     memset(dp,0x3f,sizeof dp);33     dp[0]=0;34     int hd=0,tl=0;35     q[hd]=0;36     for(i=1;i<=n;i++){37         while(hd<tl && gup(q[hd],q[hd+1])>=(sumt[i]+s)*gdown(q[hd],q[hd+1]) )38             hd++;39         dp[i]=gdp(i,q[hd]);40         while(hd<tl && gup(i,q[tl])*gdown(q[tl],q[tl-1])<=gup(q[tl],q[tl-1])*gdown(i,q[tl]) )tl--;41         q[++tl]=i;42     }43     printf("%d",dp[n]);44     return 0;45 }

 

洛谷P2365 任务安排 [解法二 斜率优化]