DP怎么这么多优化 - -
题意:
在一条路上,有很多个站,每两个站有一条路,然后给你m个炸弹,要你炸掉这些路使得能互相连通的站的和最小,如果剩下一个站,那么价值是0,假如有a,b,c三 个站,价值就是a * b + b * c + a * c
分析:
dp[i][j] 表示从1到i炸毁k条路后所需的最小值,
状态方程dp[i,k]=min(dp[j,k-1]+cost[j+1,i])(0<j<i ,0<k<=m)
cost[i,j] 表示不炸掉 i 到 j 这一段的值
cost[i,j]=cost[i,j-1]+( sum[j-1]-sum[i-1] ) * a[j];
sum[i]表示1到i站的权值和
四边形不等式优化资料:http://blog.csdn.net/lmyclever/article/details/6677683
分析一下cost[i,j],当 j 固定时,cost[i,j]的值是单调递减的,看看dp函数是否满足四边形不等式:
证明:当j固定时,cost[i,j-1],sum[j-1], a[j]的值固定,sum[i-1]随着i的值增大而增大,但是前面有个负号,所以cost[i,j]的值是单调递减
dp[i][j]= min{dp[i-1][k]+cost(k+1,j)},s[i-1][j]<=k<=s[i][j+1],复杂度降为O(m*n )
s[i][j]表示 [i,j]这个区间的最优决策
//AC CODE:参照牛人的~
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; const int N = 1000+5; int sum[N];//1到N的和 int num[N];//输入数据 int cost[N][N];//[i,j]间的站点的权值 int s[N][N];//s[i][j]表示[i,j]这个区间的最优决策 //s[i][j],要看状态转移的前一个区间,跟后一个区间,利用区间单调性可以知道, int dp[N][N];//状态方程dp[i,k]=min(dp[j,k-1]+cost[j+1,i])(0<j<i ,0<k<=m) int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0)break; int i,j,k; memset(cost,0,sizeof(cost)); sum[0] = 0; num[0] = 0; for(i=1; i<=n; i++) { scanf("%d",&num[i]); sum[i]=sum[i-1]+num[i]; } for(i=1; i<=n; i++) for(j=i+1; j<=n; j++) { cost[i][j]=cost[i][j-1]+(sum[j-1]-sum[i-1])*num[j]; } for(i=0; i<=n; i++)//前面几个站炸掉几条路 { dp[i][0]=cost[1][i];//炸掉0条路 前i条路,不炸掉的权值 cost[1,i] s[i][0]=0;//前i条路,不炸掉的状态 } for(k=1; k<=m; k++)//炸路的条数 { s[n+1][k]=n-1;//最后的一个状态(s[n][k])的后一个状态 for(i=n; i>k; i--) { dp[i][k]=dp[k][k-1]+cost[k+1][i]; //未优化前需要遍历dp[j,k-1]+cost[j+1,i] (0<j<i ,0<k<=m) //j固定时,cost[j+1,i]为减函数 s[i][k]=k;//???炸掉第k条路??? //因为这里你需要的是j+1,因此就可以证明上一个循环中应该从后往前推,因为要先计算s[i][j+1]的值 for(j=s[i][k-1]; j<=s[i+1][k]; j++)//前一状态:s[i][k-1]现在的状态:s[i][k]后一状态:s[i+1][k] { int temp = dp[j][k-1]+cost[j+1][i]; if(temp<dp[i][k]) { dp[i][k]=temp; s[i][k]=j;//前一状态,后一状态之间炸掉 最优的路 } } } } printf("%d\n",dp[n][m]); } return 0; }