石子合并 区间DP (经典)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1021

 

 设sum[i][j]为从第i为开始,长度为j的区间的值得和。dp[i][j]为从i开始长度为j的区间的最优值。

那么我们分析一下情况:

  dp[1][1] = 0,dp[2][1] = 0,dp[3][1] = 0,dp[4][1] = 0;(初始化)

两两合并的过程:

  dp[1][2] = dp[1][1] + dp[2][1] + sum[1][2];

  dp[2][2] = dp[2][1] + dp[3][1] + sum[2][2];

  dp[3][2] = dp[3][1] + dp[4][1] + sum[3][2];

  dp[4][2] = dp[4][1] + dp[5][1] + sum[4][2];

三个合并的过程:

  dp[1][3] = min(dp[1][1]+dp[2][2],dp[1][2]+dp[3][1])+sum[1][3];

  dp[2][3] = min(dp[2][1]+dp[3][2],dp[2][2]+dp[4][1])+sum[2][3];

以此类推,最后得到dp[1][n]。

可以发现每次用到的值都可以有前一阶段得到。

#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 99999999
#define mod 1000000007
#define ll __int64
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define key_value ch[ch[root][1]][0]
using namespace std;
const int MAXN = 1100;
int dp[MAXN][MAXN],sum[MAXN][MAXN],a[MAXN];
int main()
{
        int n;
        while(cin >>n){
            memset(sum,0,sizeof(sum));
            for(int i = 1; i <= n; i++){
                cin >>a[i];
            }
            memset(sum,0,sizeof(sum));//从i开始长为j的区间的和
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= n; j++){
                    sum[i][j] = sum[i][j-1] + a[j+i-1];
                }
            }

            memset(dp,0,sizeof(dp));
            for(int len = 2; len <= n; len++){//区间长度
                for(int i = 1; i <= n - len + 1; i++){//第i个开始
                    dp[i][len] = INF;
                    for(int j = 1; j < len; j++){//长度
                        dp[i][len] = min(dp[i][len],dp[i][j] + dp[i+j][len-j] + sum[i][len]);
                    }
                }
            }
            cout<<dp[1][n]<<endl;
        }
}

 

posted @ 2015-11-05 16:02  sweat123  阅读(595)  评论(0编辑  收藏  举报