石子合并

 假设只有2堆石子,显然只有1种合并方案  如果有3堆石子,则有2种合并方案,((1,2),3)和(1,(2,3))  如果有k堆石子呢? 

                           

不管怎么合并,总之最后总会归结为2堆,如果我们把最后两堆分开,左边和右边无论怎么合并,都必须满足最优合并方案,整个问题才能得到最优解。

令m[i,j]表示归并第i个数到第j数的最小代价,w[i,j]表示第i个数到第j个数的和,这个可以事先计算出来。有如下的状态转移方程: 

                 

预处理w[i][j]:  

先算出前缀和s[i],然后w[i][j] = s[j] – s[i-1]  

因此,w[i][j]可以不事先存放,在DP时直接O

(1)计算   s[0] = 0;   for(int i = 1; i<=n; i++) s[i] = s[i-1]+a[i]; 

拓展成环:  

对于环状序列,通用的处理方法是把这条链延长2倍,扩展成2n-1堆,其中第1堆与n+1堆完全相同,第i堆与n+i堆完全相同,这样我们只要对这2n堆动态规划后,枚举f(1,n),f(2,n+1),…,f(n,2n-1)取最优值即可即可。 

                         

 

#include<bits/stdc++.h>
using namespace std;
int dp[305][305],len,a[305],n,sum[305];
int main()
{
    cin>>n;
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        dp[i][i]=0;
    }
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=n-len+1;i++)
        {
            int j=i+len-1;
            for(int k=i;k<j;k++)
            {
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    cout<<dp[1][n];
}