Codeforces Round #554 (Div. 2) D.Neko and Aki's Prank(线性dp+无后效性)

题目

给定长2*n(n<=1000)的合法的括号序列构成的trie树,

求该trie树的最大独立边集,trie树样例如下图所示,蓝色边为n==2时的最大值为3

思路来源

归神

题解

dp[l][r][op]表示当前用了l个左括号,r个右括号,当前括号选不选的后继局面能选的括号的最大值

注意到dp的无后效性,也就是(())和()()这两个序列再往后面补括号面临的情形是一样的

所以只需记录当前用掉的左序号数量和右序号数量即可,降复杂度O(2^{n})O(n^{2})

op表示当前括号选不选,当前选的话,与它相邻的下一个就只能不选

至于为什么答案不是max(dp[1][0][0],dp[1][0][1]+1),大概是因为贪心地选第一个更优叭

dp总是这样,代码巨短,然而想不出来,GG

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e3+5;
int n;
ll dp[maxn][maxn][2];
ll dfs(int l,int r,int op)//op==1或op==0代表当前边选不选 
{
	//注意到当前使用的左右括号相同时,后继是相同的,无后效性 
	if(~dp[l][r][op])return dp[l][r][op];
	ll ans=0; 
	if(l<n)ans=(ans+dfs(l+1,r,!op)+!op)%mod;//放左括号
	if(l>r)ans=(ans+dfs(l,r+1,!op)+!op)%mod;//放右括号
	return dp[l][r][op]=ans; 
}
int main()
{
	scanf("%d",&n);
	memset(dp,-1,sizeof dp);
	printf("%lld\n",(dfs(1,0,1)+1)%mod);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89786594