7-10 石子合并
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。
输入格式:
数据的第 1 行是正整数 N ,表示有 N 堆石子。
第 2 行有 N 个整数,第 i 个整数 ai 表示第 i 堆石子的个数。
输出格式:
输出共 2 行,第 1 行为最小得分,第 2 行为最大得分。
输入样例:
4
4 5 9 4
输出样例:
43
54
思路:分两种解法
第一种DP格式为(dp[i][j]=min(dp[i][j],dp[i][k]+dp[i+k][j-k]+sth))
dp[i][j]为从位置i开始,累加j堆石子的结果
k将区间分割为左右,左区间累加k堆,右区间累加j-k堆,先分别在两个区间合并即得出dp[i][k]与dp[i+k][j-k],再合并区间加上本次合并产生的值即sth
(比较直接合并和k分割合并那个值是我们要的)
PS:本题解中采用将环形拓展成2倍链状,以数组存储前缀和的方式利用差值进行区间内堆累加的计算,也可采用mod的方式实现环形DP
#include<bits/stdc++.h>
using namespace std;
int n;
int a[510],sum[510];
//sum[i]以前缀和形式,记录前i堆石子和
int dp_min[510][510],dp_max[510][510];
//dp[i][j]为从位置i开始,累加j堆石子的结果
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
dp_min[i][1]=dp_max[i][1]=0;
//每个位置只合成自己是0
}
for(int i=n+1;i