石子合并

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入格式

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例
输入 #1

4
4 5 9 4

输出 #1

43
54

这其实就是一个区间dp的入门题,首先应该用到拆环,首先解释一下什么叫做拆环
1–2--3–4假如说是一个环形结构,那么4相邻的是1和3 1相邻的是2和4
所以直接复制一倍,接在后面
如上面的例子 1-2-3-4 1-2-3-4-1-2-3-4 发现满足。
那么接下来就可以写转移方程了,强烈建议读者自己调试转移方程这个东西,看是看不懂的。只有自己调
可以发现每一个堆都是一个连续的区间,都可用左右端点来表示,那么设它的最大代价
dp[i][j], 最小代价dp1[i][j],转移方程在代码里面。

//f[i][j]表示i j完全合并最多能得到的 f[i][j]=max(f[i][k]+f[k+1][j] )+和 因为先把这两堆各自合并,然后再合并这两堆
#include<bits/stdc++.h>
using namespace std;
const int maxn=202;
int a[maxn],b[maxn*2+20],sum[maxn*2+20];
int dp[maxn*2+10][maxn*2+10],dp1[maxn*20][maxn*20];
int main() {
	int n;
	cin>>n;
	int ans_max=0,ans_min=INT_MAX;
	for(int i=1; i<=n; i++)cin>>a[i];
	int now=0;
	for(int i=1; i<=n; i++)b[++now]=a[i],sum[now]=a[i]+sum[now-1];
	for(int i=1; i<=n; i++)b[++now]=a[i],sum[now]=a[i]+sum[now-1];//拆环的话直接复制一倍,接在后面 
	for(int k=1; k<=n; k++) {
		for(int r=1; r+k-1<=n*2; r++) {
			int ans=0,need=sum[k+r-1]-sum[r-1],ans1=INT_MAX;
			for(int j=r; j<=k+r-2; j++) {
				int ans0=0,ans00=0;
				if(r!=j)ans0+=dp[r][j],ans00+=dp1[r][j];
				if(r+k-1!=j+1)ans0+=dp[j+1][r+k-1],ans00+=dp1[j+1][r+k-1];
				ans=max(ans,ans0);
				ans1=min(ans1,ans00);
			}
			if(k==1||k==2)ans=0,ans1=0;
			dp[r][k+r-1]=ans+need;
			dp1[r][k+r-1]=ans1+need;
			if(k==n) { 
				ans_max=max(ans_max,dp[r][r+k-1]);
				ans_min=min(ans_min,dp1[r][r+k-1]);
			}

		}

	}
//	for(int i=1; i<=n; i++)
		//for(int j=1; j<=n; j++)
		//cout<<"dp1["<<i<<"]["<<i+j-1<<"]="<<dp1[i][i+j-1]<<endl;
	cout<<ans_min<<endl<<ans_max;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值