石子合并(一)
时间限制: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]);
}
}