题目链接: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]表示第i个位置炸成j段的最小价值,cost[i][j]表示i~j这段的价值,则得到状态转移方程:
dp[i][j]=min{dp[k][j-1]+cost[k+1][i]}.(k<i),复杂度为O(n^3)显然会超时
易得cost[1][i]=cost[1][k]+cost[k+1][i]+sum[k]*(sum[i]-sum[k])
则cost[k+1][i]=cost[1][i]-cost[1][k]+sum[k]^2-sum[k]*sum[i].
使用cost[i]表示cost[1][i]得,dp[i][j]=min{dp[k][j-1]+cost[i]-cost[k]+sum[k]^2-sum[k]*sum[i]}.
可以得出 y=dp[k][j-1]-cost[1][k]+sum[k]^2
x=sum[k].
斜率sum[i]。
用斜率优化,将复杂度降为O(n^2)即可。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int N=1e3+5; 6 int dp[N][N],sum[N],cost[N],q[N],head,tail; 7 8 //yj-yk/xj-xk 9 double Slope(int m,int k,int j){ 10 return double(dp[j][m-1]+sum[j]*sum[j]-cost[j]-dp[k][m-1]-sum[k]*sum[k]+cost[k])/(sum[j]-sum[k]); 11 } 12 13 //dp[i][j]=min{dp[k][j-1]+cost[i]-cost[k]-sum[k]*(sum[i]-sum[k])} 14 int getDP(int i,int j,int k){ 15 return dp[k][j-1]+cost[i]-cost[k]-sum[k]*(sum[i]-sum[k]); 16 } 17 18 int main(){ 19 int n,m; 20 while(~scanf("%d%d",&n,&m)){ 21 if(m==0&&n==0) 22 break; 23 memset(cost,0,sizeof(cost)); 24 sum[0]=0; 25 for(int i=1;i<=n;i++){ 26 scanf("%d",&sum[i]); 27 sum[i]+=sum[i-1]; 28 } 29 for(int i=1;i<=n;i++){ 30 for(int j=1;j<i;j++){ 31 cost[i]+=(sum[j]-sum[j-1])*(sum[i]-sum[j]); 32 } 33 dp[i][1]=cost[i]; 34 } 35 for(int j=2;j<=m+1;j++){ 36 head=tail=0; 37 q[tail++]=j-1; 38 for(int i=j;i<=n;i++){ 39 while(head+1<tail&&Slope(j,q[head],q[head+1])<=sum[i]){ 40 head++; 41 } 42 dp[i][j]=getDP(i,j,q[head]); 43 while(head+1<tail&&Slope(j,q[tail-2],q[tail-1])>=Slope(j,q[tail-1],i)){ 44 tail--; 45 } 46 q[tail++]=i; 47 } 48 } 49 printf("%d ",dp[n][m+1]); 50 } 51 return 0; 52 }