石子合并 区间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; } }