「题解」石子合并1、2

石子合并1

 题目描述

 N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
 例如: 1 2 3 4,有不少合并方法
 1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
 1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
 1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
 括号里面为总代价可以看出,第一种方法的代价最低,现在给出N堆石子的数量,计算最小合并代价。

 输入格式

 第1行:N
 第2行:有N个空格分开的整数,表示N堆石子的数量

 输出格式

 输出最小合并代价

 样例

样例输入:

4
1 2 3 4

样例输出:

19

 分析

 这道题嘛,有恒心有毅力的人当然可以搜索,通法嘛,但也可以用区间DP做
 其实呢,我们的目标就是把所有的石子合为一堆,由此我们需要将最后两大堆石子合为一堆,这两堆石子又是由更少的两堆石子合成。。。
 我们倒过来,令第一级石子为原石子,第二级石子是由任意两堆连续的第一级石子合成,同理第三级石子是由任意两堆连续的第二级和第一级石子合成,以此类推,最后,第N级石子是由任意两堆连续的第N-1级以下石子合成
因此,我们要合成第l堆和第r堆石子,我们要使得第l堆所在的大堆与第r堆所在的大堆相邻,故我们定义一个k用于枚举这两大堆在何处相邻最大,同时我们定义f[l,r]表示合并第l和r堆所需要的代价(不包含大于r或小于l部分)
 状态:f[l,r]
 状态转移方程:
f [ l , r ] = { 0 , l = = r f [ l , k ] + f [ k + 1 , r ] + s u m [ i , j ] , l < r f[l,r]=\left\{ \begin{aligned} 0, l == r \\ f[l,k]+f[k+1,r]+sum[i,j],l<r\\ \end{aligned} \right.
目标:f[1,n]

 代码

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

const int M = 1e2 + 5;
int a[M];
int pre[M];
int f[M][M];

int main() {
	int n;
	scanf("%d", &n);
	memset(f, 0x3f, sizeof(f));
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &a[i]);
		f[i][i] = 0;
		pre[i] = pre[i - 1] + a[i];//实现O(1)求sum(i,j)
	}
	for(int i = 2; i <= n; i ++) {
		for(int l = 1; l <= n - i + 1; l ++) {
			for(int k = l, r = l + i - 1; k < r; k ++) {
				f[l][r] = min(f[l][k] + f[k + 1][r] + pre[r] - pre[l - 1], f[l][r]);
			}
		}
	}
	printf("%d", f[1][n]);
	return 0;
} 

石子合并2

 题目描述

 将N堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
 请编写一个程序,读入堆数N及每堆的石子数,并进行如下计算:
 1.选择一种合并石子的方案,使得做N-1次合并得分总和最大。
 2.选择一种合并石子的方案,使得做N-1次合并得分总和最小。

 输入格式

 输入第一行一个整数N,表示有N堆石子。
 第二行N个整数,表示每堆石子的数量ai

 输出格式

 输出共两行:
  第一行为合并得分总和最小值,
  第二行为合并得分总和最大值。

 样例

样例输入

4
4 5 9 4

样例输出

43
54

分析

我们用倍长的方法,将a1~n 延长至a1~2*n-1 ,用a1~n 、a2~n+1 、a3~n+2…来比较求答案

代码

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

const int M = 2e2 + 5;
int a[M];
int pre[M];
int f[M][M];
int g[M][M];

int main() {
	int n;
	scanf("%d", &n);
	memset(f, 0x3f, sizeof(f));
	memset(g, -1, sizeof(g));
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &a[i]);
		f[i][i] = 0;
		g[i][i] = 0;
		pre[i] = pre[i - 1] + a[i];
	}
	for(int i = n + 1; i <= 2 * n - 1; i ++) {
		f[i][i] = 0;
		g[i][i] = 0;
		pre[i] = pre[i - 1] + a[i - n];
	}
	for(int i = 2; i <= n; i ++) {
		for(int l = 1; l <= 2 * n - 1 - i; l ++) {
			for(int k = l, r = l + i - 1; k < r; k ++) {
				f[l][r] = min(f[l][k] + f[k + 1][r] + pre[r] - pre[l - 1], f[l][r]);
				g[l][r] = max(g[l][k] + g[k + 1][r] + pre[r] - pre[l - 1], g[l][r]);
			}
		}
	}
	int ans1 = 2147483647;
	int ans2 = 0;
	for(int i = 1; i < n; i ++) {
		ans1 = min(f[i][i + n - 1], ans1);
		ans2 = max(g[i][i + n - 1], ans2);
	}
	printf("%d\n%d", ans1, ans2);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/yu_______chen/article/details/107433659