合并石子(区间dp)

【题目描述】
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

计算出将N堆石子合并成一堆的最小得分。

【输入】
第一行为一个正整数N (2≤N≤100);

以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1≤i≤N)。

【输出】
一个正整数,即最小得分。

【输入样例】
7
13
7
8
16
21
4
18
【输出样例】
239

题目分析:

这是一道区间dp模板题,对于我这样初学者来讲有不错的学习价值。
根据动态规划的特点,我们要从问题的本质出发,无论石子堆数多么大,最终都会转换成两堆石子合并,所以我们就可以把一开始的所有石子划分成两部分,而划分的间断点是不确定的,我们可以枚举所有情况。设dp[i][j]表示从i到j合并石子的最小花费。我们最后需要得到的答案是dp[1][n],所以我们可以通过递推来得到这个答案,其中1就是区间的起点,而n则是终点,那么我们就枚举所有起点以及所有终点。
从而得到状态转移方程:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum(i,j)).
其中sum(i,j)为从i到j合并石子的所有花费,可以用前缀和来搞定。

#include
#include
using namespace std;
int s[101];
int dp[101][101];
int main()
{
    int n,fen;
    memset(dp,127,sizeof(dp));
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>fen;
        s[i]=s[i-1]+fen;
        dp[i][i]=0;//一堆石子和自己合并不需要任何花费
    }
    for(int i=n-1;i>=1;i--)//枚举所有起点
    {
        for(int j=i+1;j<=n;j++)//枚举所有终点
        for(int k=i;k<=j-1;k++)//枚举所有分割点
        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
    }
    cout<<dp[1][n];
    return 0;
}

你可能感兴趣的:(动态规划)