HDU 2829 Lawrence(四边形优化DP O(n^2))

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2829

题目大意:有一段铁路有n个站,每个站可以往其他站运送粮草,现在要炸掉m条路使得粮草补给最小,粮草补给的公式是将每个站能收到的粮草的总和。

4----5-----1-----2

粮草总和为4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49.

4----5       1-----2

粮草总和为4*5 + 1*2 = 22. 

4      5-----1------2

粮草总和为5*1 + 5*2 + 1*2 = 17.

解题思路:参考,状态转移方程为dp[i][j]=min{dp[k][j-1]+cost[k+1][i]}(k<i),若cost[i][j]满足凸四边形不等式,则可以用四边形优化。

     这里我们有一个定理:cost为凸当且仅当 cost[i][j] + cost[i+1][j+1] <= cost[i+1][j] + cost[i][j+1]。

     我们可以把原式cost[i][j] + cost[i'][j'] <= cost[i][j'] + cost[i'][j]变为cost[i + 1][j + 1] - cost[i + 1][j] <= cost[i][j + 1] - cost[i][j],然后固定j变化i,看coat[i][j+1] - cost[i][j]是关于i递增还是递减,如果是递减,则cost为凸。

     一般如果不能直接看出来,可以进行打表。此题cost[i][j]满足该定理,于是可以用四边形优化将复杂度降至O(n^2)。

     注意:这里的是s[i][j]处理跟石子归并时不一样,还有i是倒着来的,不是正着的。

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 typedef long long LL;
 5 const int N=1e3+5;
 6 const long long INF=0x3f3f3f3f;
 7 LL sum[N],dp[N][N],cost[N][N],s[N][N];//s[i][j]记录dp[i][j]的最优切割点 
 8 
 9 int main(){
10     int n,m;
11     while(~scanf("%d%d",&n,&m)){
12         if(n==0&&m==0)
13             break;
14         memset(cost,0,sizeof(cost));
15         memset(dp,INF,sizeof(dp));
16         for(int i=1;i<=n;i++){
17             scanf("%lld",&sum[i]);
18             sum[i]+=sum[i-1];
19             s[i][0]=0;
20         }
21         //计算cost[i][j]
22         for(int i=1;i<=n;i++){
23             for(int j=1;j<i;j++){
24                 cost[1][i]+=(sum[j]-sum[j-1])*(sum[i]-sum[j]);
25             }
26             dp[i][0]=cost[1][i];
27         }
28         for(int k=1;k<=n;k++){
29             for(int i=k+1;i<=n;i++){
30                 cost[k][i]=cost[1][i]-cost[1][k-1]-sum[k-1]*(sum[i]-sum[k-1]);
31             }
32         }
33         
34         //j为轰炸次数,当i = 1或者j = n时为边界对s的处理就是为了处理这些边界
35         for(int j=1;j<=m;j++){
36             s[n+1][j]=n-1;
37             for(int i=n;i>=j;i--){
38                 for(int k=s[i][j-1];k<=s[i+1][j];k++){
39                     LL tmp=dp[k][j-1]+cost[k+1][i];            
40                     if(tmp<dp[i][j]){
41                         dp[i][j]=tmp;
42                         s[i][j]=k;
43                     }
44                 }
45             }    
46         }
47         printf("%lld
",dp[n][m]);
48     }
49     return 0;
50 }
原文地址:https://www.cnblogs.com/fu3638/p/7822935.html