nyoj 737 石子合并(区间DP)
737-石子合并(一)
内存限制:64MB
时间限制:1000ms
特判: No
通过数:28
提交数:35
难度: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
题目大意:
给你n堆石子,每堆石子有ai个。你每次可以把两堆石子合并到一块,代价为总的石子数。问最少需要多少代价将n堆石子合为一堆。
最基本的区间dp问题。枚举区间长度,区间,和划分。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=200; const int inf=1000000000; int a[maxn+10]; int sum[maxn+10][maxn+10]; int dp[maxn+10][maxn+10]; int main() { int n; while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",a+i); for(int i=2;i<=n;i++) a[i]+=a[i-1]; a[0]=0; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) sum[i][j]=a[j]-a[i-1]; memset(dp,0,sizeof(dp)); for(int len=2;len<=n;len++)//直接跳过长度为1的区间,默认为0 { for(int left=1;left<=n;left++) { int right=left+len-1; if(right>n) break; int val=inf; for(int mid=left;mid<right;mid++) val=min(val,dp[left][mid]+dp[mid+1][right]); dp[left][right]=val+sum[left][right]; //printf("%d %d %d\n",left,right,dp[left][right]); } } printf("%d\n",dp[1][n]); } return 0; }