1. 程式人生 > >石子合併問題--直線版 HRBUST

石子合併問題--直線版 HRBUST

一條直線上擺放著一行共n堆的石子。現要將石子有序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆石子數記為該次合併的得分。請編輯計算出將n堆石子合併成一堆的最小得分和將n堆石子合併成一堆的最大得分。

Input

輸入有多組測試資料。

每組第一行為n(n<=100),表示有n堆石子,。

二行為n個用空格隔開的整數,依次表示這n堆石子的石子數量ai(0<ai<=100)

Output

每組測試資料輸出有一行。輸出將n堆石子合併成一堆的最小得分和將n堆石子合併成一堆的最大得分。 中間用空格分開。

Sample Input

3

1 2 3

Sample Output

9 11

思路:合併這兩堆前,這兩堆已經得到的分數 + 合併這兩堆得的分數;列舉分割點,找出合併這兩堆前,這兩堆已經得到的分數的最大和最小;

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define Max 110
#define ll long long 
#define INF 0x3f3f3f3f
int sum[Max];
int dp1[Max][Max];
// 合併這兩堆前,這兩堆已經得到的分數 + 合併這兩堆得的分數; 
int dp2[Max][Max];
int n;
int main()
{
	
	while(~scanf("%d",&n))
	{
		sum[0] = 0;
		memset(dp1,INF,sizeof(dp1));
		memset(dp2,0,sizeof(dp2));
		for(int i = 1;i <= n;i ++)
		{
			scanf("%d",&sum[i]);
			sum[i] +=sum[i-1];
			dp1[i][i] = 0;
		}
		for(int len = 2;len <= n;len ++)
		{
			for(int i = 1,j = len; j <= n; i++ ,j++)
			{
				for(int k = i;k<=j-1;k++)  // 列舉分割點; 
				{
					dp1[i][j] = min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[j]-sum[i-1]);
					dp2[i][j] = max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]);
				}
			}
		}	
		printf("%d %d\n",dp1[1][n],dp2[1][n]);
	}	
	return 0;
}