1 | 2 | … | n | n+1 | |
---|---|---|---|---|---|
1 | 0 | sum[1] [2] | … | 线、环形排列答案位置 | … |
2 | 0 | … | … | 环形答案位置 | |
3 | 0 | … | … | ||
4 | 0 | … | |||
n | … |
1 | 2 | 3 | … | n-1 | n | n+1 | n+2 |
---|---|---|---|---|---|---|---|
a[1] | a[1]+a[2] | a[1]+a[2]+a[3] | … | ∑a[1 : n-1] | ∑a[1 : n] | a[1] | … |
… | ∑a[1 : n-1] | ∑a[1 : n] | a[1]+∑a[i : n-1] | … |
例5:石子合并问题**(例题)
问题描述: 在一个圆形操场的四周摆放着n堆石子. 现在要将石子有次序地合并成一堆. 规定每次只能选相邻的2堆石子合并成一堆, 并将新的一堆石子数记为该次合并的得分. 试设计一个算法, 计算出将n堆石子合并成一堆的最小得分和最大得分.
一、确定状态:
终点:
子问题:设f[i] [j]为从i到j最小得分,g[i] [j]为从i到j最大得分;
二、确定转移方程:
f[i] [j] = min{f[i] [k] , f[k+1] [j]} + sum[i] [j]
g[i] [j] = max{f[i] [k] , f[k+1] [j]} + sum[i] [j]
三、确定初始条件和边界情况:
初始条件:f[0] [0] = g[0] [0] = 0;
边界情况:f[i] [i+1] = g[i] [i+1] = sum[i] [i+1]
四、确定计算顺序:
从小到大,填表斜行
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int f[205][205];
int g[205][205];
int a[205];
int main()
{
int i, j, k;
int tmp, MAX, MIN;
int n;
cin >> n;
for (i = 1; i <= n; i++)
{
cin >> tmp;
a[i] = a[i - 1] + tmp;
a[i + n] = tmp;
}
for (i = n + 1; i <= 2 * n; i++)
a[i] += a[i - 1];
for (k = 1; k <= n - 1; k++)
for (i = 1; i <= 2 * n - k; i++)
{
MIN = 0x3f3f3f;
MAX = 0;
for (j = i + 1; j <= i + k; j++)
{
tmp = a[i + k] - a[i - 1] + f[i][j - 1] + f[j][i + k];
MAX = max(tmp,MAX);
tmp = a[i + k] - a[i - 1] + g[i][j - 1] + g[j][i + k];
MIN = min(tmp, MIN);
}
f[i][i + k] = MAX;
g[i][i + k] = MIN;
}
MAX = 0;
MIN = 0x3f3f3f;
for (i = 1; i <= n; i++)
{
if (f[i][i + n - 1] > MAX)
MAX = f[i][i + n - 1];
if (g[i][i + n - 1] < MIN)
MIN = g[i][i + n - 1];
}
cout << MIN << endl;
cout << MAX << endl;
}
总结
这道oj的题花了我很长时间,原因是没有看到题目中圆形二字,一直以为是直线排列,导致结果一直不符合测例结果又想不出原因,而进一步来说圆形意味着首尾相连,只需要在原基础上在n后复制n之前的数据即可,另外最后输出结果要注意因为是环形排列最值位置不再是f[1] [n]位置,而是会出现在f[1] [n] ~ f[n] [2n-1]位置,需要简单遍历判断 。