题目链接 石子合并
题目描述
在一个圆形操场的四周摆放
N
N
N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的
2
2
2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将
N
N
N 堆石子合并成
1
1
1 堆的最小得分和最大得分。
输入格式
数据的第
1
1
1 行是正整数
N
N
N,表示有
N
N
N 堆石子。
第
2
2
2 行有
N
N
N 个整数,第
i
i
i 个整数
a
i
a_i
ai 表示第
i
i
i 堆石子的个数。
输出格式
输出共 2 2 2 行,第 1 1 1 行为最小得分,第 2 2 2 行为最大得分。
输入输出样例
输入 #1
4
4 5 9 4
输出 #1
43
54
说明/提示
1 ≤ N ≤ 100 1\leq N\leq100 1≤N≤100 , 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0≤ai≤20。
题目解析:
让我们先看看样例: 这是最小值的求法,看起来好像和贪心很像啊!!!但其实只是样例数据给出的假象罢了.
很多的大佬和蒟蒻做题时用了贪心结果只有30分!
首先如何解决上图环的问题呢?
当然很简单啦,我们把它存成一条链:即把
T
T
T存成
2
∗
T
2*T
2∗T
如:2 3 4 6 5 4 2 3 4 6 5 4 这样每次枚举
i
i
i到
i
+
N
−
1
i+N-1
i+N−1就可以了是吧是不是很简单啊(
i
≤
N
i\leq N
i≤N)
上文我们说到这是一题动规,那么我们来分析一下:
-
根据题意可知每次都是两堆石子合并成一堆,并且这两堆石子是相邻的!!
那么这两堆石子又是由另外的石子合并的,那么我们可以认为i到j堆石子是由 F [ i ] [ k ] F[i][k] F[i][k]和 F [ k + 1 ] [ j ] F[k+1][j] F[k+1][j]合成的。那么 F [ i ] [ k ] F[i][k] F[i][k]也是根据上面的规则求得到! -
那么合成的分数如何表示的呢?
已知每个点的分数都是确定的,那么无论前面的数据如何合并的分数一定是由前面 s u m [ j ] − s u m [ i − 1 ] sum[j]-sum[i-1] sum[j]−sum[i−1]的值, s u m [ i ] = s u m [ i − 1 ] + T [ i ] sum[i]=sum[i-1]+T[i] sum[i]=sum[i−1]+T[i]; 因此得到 F [ i ] [ j ] = m a x ( F [ i ] [ k ] + [ k + 1 ] [ j ] + s u m [ j ] − s u m [ i − 1 ] , F [ i ] [ j ] ) F[i][j]=max(F[i][k]+[k+1][j]+sum[j]-sum[i-1],F[i][j]) F[i][j]=max(F[i][k]+[k+1][j]+sum[j]−sum[i−1],F[i][j]); 至此这题大水题已经解决了剩下的只是要考虑合并几次的问题而已
因为第一次至少两堆合并,那么就有了
L
(
L
=
2
;
L
<
=
N
;
+
+
L
)
j
=
i
+
L
−
1
L (L=2;L<=N;++L)j=i+L-1
L(L=2;L<=N;++L)j=i+L−1;
最后就是求一下答案枚举一遍就行了
代码
/*************************************************
Note:
*************************************************/
#include <queue>
#include <stack>
#include <set>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#define ll long long
#define ull unsigned long long
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MAXN = INF, MINN = -INF;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int T[210], F1[210][210], F2[210][210], sum[210];
int main(void)
{
int N = read();
for (int i = 1; i <= N; ++i)
T[i] = read(), T[i + N] = T[i];
for (int i = 1; i <= 2 * N; ++i)
sum[i] = sum[i - 1] + T[i], F1[i][i] = 0, F2[i][i] = 0;
for (int L = 2; L <= N; ++L)
{
for (int i = 1; i <= 2 * N - L + 1; ++i)
{
int j = i + L - 1;
F1[i][j] = MAXN, F2[i][j] = MINN;
for (int k = i; k < j; ++k)
{
F1[i][j] = min(F1[i][k] + F1[k + 1][j], F1[i][j]);
F2[i][j] = max(F2[i][k] + F2[k + 1][j], F2[i][j]);
}
F1[i][j] += (sum[j] - sum[i - 1]);
F2[i][j] += (sum[j] - sum[i - 1]);
}
}
int ANS1 = MAXN, ANS2 = MINN;
for (int i = 1; i <= N; ++i)
{
ANS1 = min(ANS1, F1[i][i + N - 1]);
ANS2 = max(ANS2, F2[i][i + N - 1]);
}
printf("%d\n%d", ANS1, ANS2);
return 0;
}