BZOJ 1996 合唱队 - 区间DP(以及DP注意事项)

本文探讨了一种基于区间动态规划的问题解决策略,并利用RMQ技术进行优化。通过对每个数值的逐一插入,实现区间两端的扩展,确保相邻数值满足特定条件。文章详细介绍了状态转移方程的设计与实现细节,包括枚举方向的选择、初始值设定等关键点。

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

很快看出来是区间dp,搞了个O(n^3)的dp方程,0/1状态表示当前区间的首/尾作为末尾,然后搞记忆化搜索半天没搞出来,还得搞个RMQ,于是百度了题解(为毛题解都长的一样,还都是WA的。。。这群人抄也不抄仔细点。。。)

题解大概就是通过一个个数插入将区间向两边扩大,而且只能放在两头,条件就是上一个放的数值和这个数的数值满足题意。讨论一下就好了,dp方程写出来还是很简单的。(其实就是将我的思路改进一下,不合并区间与区间,合并区间与数)


这里需要注意枚举区间i,j的方向,由于i+1向i拓展,j-1向j拓展,于是i+1应该在i之前,于是i指针应该倒序,同理j应该升序。

注意状态转移的方向和枚举的方向是相反的!


其次就是初值,观察列出的dp方程,发现如果j==i+1时,dp[i][i]和dp[j][j]会向dp[i][j]加两遍(因为dp[i][i]无序),所以初值只任意设一个就好了。


还有就是必须得吐槽一下网上的代码,照着写了半天,交上去WA了一屏。结果把网上的原码交上去照样WA。爆int是要闹哪样啊。。。一份两份全都爆int是要闹哪样啊(雾)。。。


#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=1005;
const int mod=19650827;

int n;
int s[maxn];
long long dp[maxn][maxn][2];

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",s+i),
		dp[i][i][1]=1;//只选0/1中一个赋值 
	for(int i=n;i;i--)
		for(int j=i+1;j<=n;j++)
		{
			if(s[i]<s[j])dp[i][j][1]+=dp[i][j-1][0];
			if(s[i]<s[j])dp[i][j][0]+=dp[i+1][j][1];
			if(s[i+1]>s[i])dp[i][j][0]+=dp[i+1][j][0];
			if(s[j]>s[j-1])dp[i][j][1]+=dp[i][j-1][1];
			dp[i][j][0]%=mod;dp[i][j][1]%mod;
		}
	printf("%d",(dp[1][n][0]+dp[1][n][1])%mod);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值