洛谷P1880 [NOI1995] 石子合并 题解

此题解以纪念我终于差不多大概搞懂区间dp了(插个存档点,到时候忘了再回来看看)。

P1880 [NOI1995] 石子合并 题解

在做这道题之前,可以看看 P1775 石子合并(弱化版)(一道题解帮你搞定两道题,多划算)。

P1775 石子合并(弱化版)

形式化的题面

一堆石头摆在你面前,让你把他们扔到一起,每次扔都要花你这两堆石头的重量的力气,让你找出最省力的方法。

思路

使用区间dp

我们先手算一遍样例(如图)

接下来我们就要将他换成一个流程,再编写成代码。

再看图,是不是我们就可以想到,枚举所有合并的区间。

  1. 第一层循环从2到 n n n 枚举区间的长度 l e n len len
  2. 第二层循环去枚举这个区间的左端点 i i i ,那么这个区间的右端点就是显而易见的 i + l e n i+len i+len
  3. 再枚举 l e n len len 长的区间枚举每个分割可能的位置 k k k ,使得 d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j ] dp[i][j] = dp[i ][ k] + dp[k + 1][j] dp[i][j]=dp[i][k]+dp[k+1][j] ;所以我们的 k k k i i i 开始枚举,到 j j j 结束 ( i < = k < j ) ( i <= k < j ) (i<=k<j)
  4. 最后注意一下循环的范围就好了
  5. 对于代价(体力),我们可以根据前缀和进行记录,在代码中,我用 s [ i ] s[i] s[i] 记录,那么区间 [ i ] [ j ] [i][j] [i][j] 的代价就是 s [ j ] − s [ i − 1 ] s[j]-s[i-1] s[j]s[i1]
根据上述思路写的代码
#include<bits/stdc++.h>
using namespace std;
int dp[310][310],s[310]={0},n,a[310];
int main()
{
	std::ios::sync_with_stdio(false);
    cin>>n;
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    	s[i]=s[i-1]+a[i];//前缀和 
    	dp[i][i]=0;
	}
	for(int i=2;i<=n;i++)//模拟区间 
	{
		for(int j=0;j<=n-i+1;j++) 
		{
			int l=j+i-1;
			for(int k=j;k<l;k++)
			{
				dp[j][l]=min(dp[j][l],dp[j][k]+dp[k+1][l]+s[l]-s[j-1]);
			}
		}
	}
	cout<<dp[1][n];
    return 0;
}

P1880 [NOI1995] 石子合并

再看一下这道题,我们只需再加一个max就好了。

请注意,这道题有环,那我们该怎么处理呢?

  • 可以通过复制一遍这个数组,环转链就行了,在这道题中,
    4 9 5 4就可以处理为4 9 5 4 4 9 5 4就行了
代码
#include<bits/stdc++.h>
using namespace std;
int dpma[310][310]={0},dpmi[310][310]={0},s[310]={0},n,a[310],ma=0,mi=0x3f3f3f;
int main()
{
	ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    	a[i+n]=a[i];
    	s[i]=s[i-1]+a[i];
	}
	for(int i=n+1;i<=2*n;i++)//环状处理 
	{
		s[i]=a[i]+s[i-1];//前缀和 
	}
	for(int len=1;len<n;len++)//区间[i,j]的长度 
	{
		for(int i=1;i<=2*n-len;i++)//枚举左端点 
		{
			int j=i+len;//右端点显而易见 
			dpmi[i][j]=0x3f3f3f;//在枚举min的时候,我们要将这个mi放大,毕竟自然数中,没有比0小的数了。 
			for(int k=i;k<j;k++)
			{
				dpmi[i][j]=min(dpmi[i][j],dpmi[i][k]+dpmi[k+1][j]+s[j]-s[i-1]);
				dpma[i][j]=max(dpma[i][j],dpma[i][k]+dpma[k+1][j]+s[j]-s[i-1]); 
				//cout<<"mi:"<<dpmi[i][j]<<" ma:"<<dpma[i][j]<<endl; 
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		//我们要得到的是整个链的结果,所以当i为起点,那么len=n,结尾就是i+n-1 
		ma=max(ma,dpma[i][i+n-1]);//整个链 
		mi=min(mi,dpmi[i][i+n-1]);
	}
	cout<<mi<<endl<<ma;
    return 0;
}
Vivado2023是一款集成开发环境软件,用于设计和验证FPGA(现场可编程门阵列)和可编程逻辑器件。对于使用Vivado2023的用户来说,license是必不可少的。 Vivado2023的license是一种许可证,用于授权用户合法使用该软件。许可证分为多种类型,包括评估许可证、开发许可证和节点许可证等。每种许可证都有不同的使用条件和功能。 评估许可证是免费提供的,让用户可以在一段时间内试用Vivado2023的全部功能。用户可以使用这个许可证来了解软件的性能和特点,对于初学者和小规模项目来说是一个很好的选择。但是,使用评估许可证的用户在使用期限过后需要购买正式的许可证才能继续使用软件。 开发许可证是付费的,可以永久使用Vivado2023的全部功能。这种许可证适用于需要长期使用Vivado2023进行开发的用户,通常是专业的FPGA设计师或工程师。购买开发许可证可以享受Vivado2023的技术支持和更新服务,确保软件始终保持最新的版本和功能。 节点许可证是用于多设备或分布式设计的许可证,可以在多个计算机上安装Vivado2023,并共享使用。节点许可证适用于大规模项目或需要多个处理节点进行设计的用户,可以提高工作效率和资源利用率。 总之,Vivado2023 license是用户在使用Vivado2023时必须考虑的问题。用户可以根据自己的需求选择合适的许可证类型,以便获取最佳的软件使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值