首页 > 代码库 > nyoj 石子归并(1)

nyoj 石子归并(1)

石子合并(一)

时间限制: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
来源
经典问题
上传者
TC_胡仁东
思路:区间dp~dp[i][j]表示从i到j的范围内合并成一堆最小的耗费~
          状态转移方程: dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
 这道题和括号匹配是一样的思路~~朴素dp O(n^3)的复杂度
PS:(1)有一种四边形优化的方案~
        (2)有另外一种变形~就是环形合并
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <cmath>
const int maxn=220;
using namespace std;
int dp[maxn][maxn];
int sum[maxn];
int main()
{
 int n;
 while(cin>>n)
 {
  for(int i=1;i<=n;i++)
   {
     cin>>sum[i];
     sum[i]+=sum[i-1];
   }
  for(int i=0;i<=n;i++)
    dp[i][i]=0;
  for(int k=1;k<n;k++)
  {
   for(int i=1;i<=n-k;i++)
    {
        dp[i][i+k]=99999999;
        for(int j=i;j<i+k;j++)
          if(dp[i][i+k]>dp[i][j]+dp[j+1][i+k]+sum[i+k]-sum[i-1])
             dp[i][i+k]=dp[i][j]+dp[j+1][i+k]+sum[i+k]-sum[i-1];
    }
  }
  cout<<dp[1][n]<<endl;
 }
 return 0;
}