区间dp以及求解这类问题的一般方法

博客介绍了求解区间dp的一般套路,包括注意范围合理性、从大范围往内推、剪枝和注意特殊情况。还以hdu 5900为例,给出题意和解题思路,定义dp数组表示最优解,列出转移方程,并指出易遗漏的情况,最后表示可据此写代码。

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

来,先上线一道题。

求解区间dp的一般套路:

1,注意范围是否合理,例如会不会越界。

2,一般是从大范围往内推直到能简单处理的小范围(小区间)。

3,剪枝,一般初始化dp,要不是初始化的值,一般就直接返回了,这也类似于记忆化搜索。

4,注意特殊情况

hdu 5900

题意:给出n组的key,value值,相邻的两个key如果它们的最大公约数不是1,那么我们可以将它们加起来,并清除。

问:找出最大的结果,可以挑选任意相邻的。

 

思路:

这里的解题思路参考此博客:https://blog.youkuaiyun.com/spark__s/article/details/52585459

dp题,用dp[[i][j]表示从i到j之间的最优解,转移方程可以这样写

i>=j的时,dp[i][j]=0; (范围越界)

i+1==j且key[i]和key[j]互质时,dp[i][j]=0;  (缩小至能够简单处理的区间)

i+1==j且key[i]和key[j]不互质时,dp[i][j]=val[i]+val[j]

其他情况为,枚举kij-1, dp[i][j]=max(dp[i][k]+dp[k+1][j]).

上面还遗漏了一种情况,就是i+1到j-1之间都可以取,且i和j不互质,这种情况i到j所有的数都可以取。

解释下,为什么会遗漏这种情况,是说(i+1)到(j-1)满足条件,并移走之后,头跟尾会变成相邻的一对,喔,这里就有点坑了,考得真细。

以上就是所有情况,接下来写代码就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;

LL key[305];
LL val[305];
LL dp[305][305];
LL gcd(LL a,LL b)
{
	return b?gcd(b,a%b):a;
}
LL cal(int x,int y)
{
	if(dp[x][y]!=-1) ///剪枝,不为-1,表示当前范围已经把最大结果计算好了,故直接返回就好了
		return dp[x][y];
	LL t=gcd(key[x],key[y]);
	if(x>=y)  ///范围越界
	{
		return dp[x][y]=0;
	}
	if(x+1==y&&t!=1) ///能简单处理的情况(小区间)
	{
		return dp[x][y]=val[x]+val[y];
	}
    if(x+1==y&&t==1) ///也是能简单处理的情况(小区间)
	{
		return dp[x][y]=0;
	}
	LL _max=0;
	if(t!=1) ///特殊情况
	{
		LL xx=0;
		for(int i=x+1;i<=y-1;i++)
		{
			xx+=val[i];
		}
		if(cal(x+1,y-1)==xx)
			_max=xx+val[x]+val[y];
	}
	for(int i=x;i<=y-1;i++) ///从大范围开始往内缩
	{///为什么i不是从x+1开始的呢?举个例子:key 2 4 8
	    ///要是从x+1开始,那么就不会计算到[2,3],显然是不行的
		_max=max(_max,cal(x,i)+cal(i+1,y));
	}
	return dp[x][y]=_max;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(dp,-1,sizeof(dp));
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++)
		{
			scanf("%lld",&key[i]);
		}
		for(int i=0;i<n;i++)
		{
			scanf("%lld",&val[i]);
		}
		LL ans=cal(0,n-1);
		printf("%lld\n",ans);
	}

}

 

我的标签:做个有情怀的程序员。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值