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;
}