题目描述
在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。 规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。 试设计一个算法,计算出将n堆石子
类似于矩阵连乘
注意由于是圆形,而矩阵链乘是线性的,所以这个递归方程势必需要修改。考虑如何表示环,一个自然的想法是利用mod函数。递归方程修改如下,为了方便表示修改dp[i,j]定义如下:从第i堆开始合并j堆的最优解。(否则需要填写的项数不全在一个矩阵半角上)
dp[i,j]=min{dp[i,k]+dp[(i+k)%n,j−k]+vi+…+vj} i<=k<=j
dp[i,j]=max{dp[i,k]+dp[(i+k)%n,j−k]+vi+…+vj} i<=k<=j
算法
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
int dp_max[207][207];
int dp_min[207][207];
int cost[207];
int a[203];
const int MAX = 0x3f3f3f3f;
int main()
{
while(scanf("%d", &n) != EOF)
{
memset(dp_min, MAX, sizeof(dp_min));
memset(dp_max, 0, sizeof(dp_max));
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
a[i + n] = a[i];
dp_min[i][i] = 0;
dp_min[i + n][i + n] = 0;
}
cost[0] = 0;
for(int i = 1; i <= 2 * n; i++)//为了后面方便用前缀和
cost[i] = cost[i - 1] + a[i];
for(int len = 2; len <= n; len++)//走几步
{
for(int i = 1; len + i - 1 <= 2 * n; i++)//从哪开始
{
int j = len + i - 1;
for(int k = i; k < j; k++)
{
dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[k + 1][j] + cost[j] - cost[i - 1]);
dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[k + 1][j] + cost[j] - cost[i - 1]);
}
}
}
int ans_min = MAX;
int ans_max = -1;
for(int i = 1; i <= n; i++)
{
ans_min = min(ans_min, dp_min[i][i+n-1]);
ans_max = max(ans_max, dp_max[i][i+n-1]);
}
cout << ans_min << " " << ans_max << endl;
}
return 0;
}