dp专辑 U - Lawrence [ 四边形不等式优化]

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






你可能感兴趣的:(c,优化)