NYOJ石子合并(一)

石子合并(一)
时间限制:1000 ms | 内存限制:65535 KB
难度:3
描述
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
输入
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开
输出
输出总代价的最小值,占单独的一行
样例输入
3
1 2 3
7
13 7 8 16 21 4 18
样例输出
9
239

题目分析:
首先我们需要先定义状态:dp[i][j]:区间[i,j]的合并石子的总的代价的最小值。
下面我们就考虑它的转移方程或者说最优子结构了:
我们可以把dp[i][j]分成两个子区间:[i,k],[k+1,j],(重叠子问题)
那么就可以知道它的转移方程了:
dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum(i,j)),(i < k < j)(最优子结构)
当然,还有一个很重要的东西就是初始状态:dp[i][i]=0,dp[i][i+1]=stone[i]+stone[i+1]
即如果只有一堆石子就不用移动了,相邻的两堆就直接相加就好。

注意从小区间推大区间
Accepted code:


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn=205;
int stone[maxn];
int dp[maxn][maxn];
int n;
int sum[maxn];

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&stone[i]);
        }
        sum[0]=0;
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+stone[i-1];

        //len=1
        for(int i=0;i<n;i++) dp[i][i]=0;
        //len=2
        for(int i=0;i<n-1;i++) dp[i][i+1]=stone[i]+stone[i+1];
        //len>=3
        for(int l=3;l<=n;l++)
        {
            for(int s=0;s+l-1<n;s++)
            {
                int t=s+l-1;
                int temp=0x3f3f3f3f;
                for(int k=s;k<t;k++)
                {
                    temp=min(temp,dp[s][k]+dp[k+1][t]+sum[t+1]-sum[s]);
                }
                dp[s][t]=temp;
            }
        }
        printf("%d\n",dp[0][n-1]);
    }
}

猜你喜欢

转载自blog.csdn.net/QingyingLiu/article/details/80687188
今日推荐