石子合并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[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;
}