P1880 [NOI1995] 石子合并(区间DP+思维)

这篇博客探讨了NOI1995石子合并问题的升级版,该问题允许合并两端的石子,并要求求解最大代价。作者介绍了从弱化版问题的解决方案出发,如何通过复制序列和区间DP优化算法,达到O(n)的时间复杂度求解最大值。博客提供了详细的解题思路和C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:[NOI1995] 石子合并 - 洛谷

这道题目是那道弱化版石子合并的升级版,建议先思考明白弱化版石子合并的解题方法,我在这篇博客直接阐述如何在弱化版的基础上解决这道题,我在之前介绍过弱化版石子合并的解题方法,下面附上博客地址:P1775 石子合并(弱化版)(区间DP)_AC__dream的博客-优快云博客

两道题的区别主要在于弱化版的石子合并问题不能合并两端的石子,而这道题可以直接合并两端的石子,当然还有一个区别就是这道题还要求出最大代价,不过原理与求最小代价基本上一模一样,甚至可以说在初始化方面还要比求最小代价还要简单,下面我来分析如何解决两端石子合并的问题:

按照原来石子合并的思考方式,对于每个f[i][j]我们是通过枚举中间值k来更新f[i][j]的,也就是说最优解一定对应一个k是在区间[i,j]上的,而这道题一开始是一个圆周,对于最优解而言也一定存在一个k在区间[1,n]上使得最优方案与链状分布k,k+1,……n,1,2,……,k-1的石子合并答案是相同的,因为对于最优方案最后一步合并我们一定是将两堆合并为一堆,而这两堆一定有一个元素是相邻的,那么相邻点就是我们要枚举的中间点,所以我们需要枚举所有中间点,最后求一个最大值一定能得到答案。但是这样做复杂度未免有点高,我们对这个问题进行如下思考:我们将原先的1~n的石子序列向右整体复制一份变为1,2……,n,1,2,……n,我们对这个石子合并问题求解长度等于n的最大值,求完之后就有f[k][k+n-1]表示序列k,k+1,……n,1,2,……,k-1的石子合并最优解,这样我们直接o(n)枚举所有的f[k][k+n-1],求一个最值即可。这个思路比较巧妙,在其他题目中也经常遇到这样的求解方法,建议大家好好理解一下,下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=303;
ll fn[N][N],fx[N][N],sum[N];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%lld",&sum[i]),sum[i+n]=sum[i];
	memset(fn,0x3f,sizeof fn);
	for(int i=1;i<=2*n;i++)
		fn[i][i]=0,sum[i]+=sum[i-1];
	for(int len=2;len<=n;len++)
	{
		for(int i=1;i+len-1<=2*n;i++)
		{
			int j=i+len-1;
			for(int k=i;k<j;k++)
			{
				fx[i][j]=max(fx[i][j],fx[i][k]+fx[k+1][j]+sum[j]-sum[i-1]);
				fn[i][j]=min(fn[i][j],fn[i][k]+fn[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
	ll ansmax=0,ansmin=0x3f3f3f3f3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		ansmin=min(ansmin,fn[i][i+n-1]);
		ansmax=max(ansmax,fx[i][i+n-1]);
	}
	printf("%lld\n",ansmin);
	printf("%lld",ansmax);
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值