洛谷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 }
原文地址:https://www.cnblogs.com/SilverNebula/p/5926270.html