石子合并
题目链接:ybt高效进阶5-2-1 / luogu P1880
题目大意
给你几堆石子排成一圈,每次选相邻的两堆合成一堆,费用是这新一堆石子的个数。
然后你要把它合到只有一堆,要你求最大费用最小费用。
思路
区间 DP 经典题。
就直接设
m
i
n
n
i
,
j
minn_{i,j}
minni,j 为把
i
i
i 到
j
j
j 的区间合并的最小费用,
m
a
x
n
i
,
j
maxn_{i,j}
maxni,j 则是最大费用。
然后就先枚举区间的长度,然后枚举区间,枚举分割的地方 DP 即可。
快速求合并费用可以用前缀和,复杂度为
O
(
n
3
)
O(n^3)
O(n3) 可过。
代码
#include<cstdio>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int n, a[201], maxn[101][101], minn[101][101], sum[101];
int ansmaxn, ansminn;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i], a[n + i] = a[i];
for (int i = n + 1; i <= n + n; i++)//记得是环状的,所以求前缀和要复制一遍再处理
sum[i] = sum[i - 1] + a[i];
for (int j = 2; j <= n; j++)//先枚举合并的区间大小
for (int st = 1; st <= n; st++) {
int ed = st + j - 1;
maxn[st][(ed <= n) ? ed : ed - n] = 0;
minn[st][(ed <= n) ? ed : ed - n] = INF;
for (int k = st; k < ed; k++) {
maxn[st][(ed <= n) ? ed : ed - n] = max(maxn[st][(ed <= n) ? ed : ed - n], maxn[st][(k <= n) ? k : k - n] + maxn[(k + 1 <= n) ? k + 1 : k + 1 - n][(ed <= n) ? ed : ed - n] + sum[ed] - sum[st - 1]);
minn[st][(ed <= n) ? ed : ed - n] = min(minn[st][(ed <= n) ? ed : ed - n], minn[st][(k <= n) ? k : k - n] + minn[(k + 1 <= n) ? k + 1 : k + 1 - n][(ed <= n) ? ed : ed - n] + sum[ed] - sum[st - 1]);
}
}
ansminn = INF;
ansmaxn = 0;
for (int i = 1; i <= n; i++) {//记得最后的环可能有很多种,所以要都枚举一次
ansminn = min(ansminn, minn[i][(i + n - 1 <= n) ? i + n - 1 : i - 1]);
ansmaxn = max(ansmaxn, maxn[i][(i + n - 1 <= n) ? i + n - 1 : i - 1]);
}
printf("%d\n%d", ansminn, ansmaxn);
return 0;
}