DP
处理括号匹配最常用的方法: 右括号+1,左括号-1,定 n == 0; 括号字符串从左向右,右括号 n+1,左括号 n-1,如果当前状态 n < 0,那么括号字符串就是不合法的, 即使后面还有括号没比较树上的匹配方法:
从叶结点开始从下至上匹配,匹配过的结点不能在匹配,这种贪心策略是正确的。
总结的结论就是,根节点从1算起,如果树的层数是奇数,那么偶数层的节点数的总和就是树的最大匹配,如果树的层数是
偶数,那么奇数层的节点数的总和就是树的最大匹配。
根据上面的结论,得出下面的做法。
dp[i][j] 代表的状态是第i层节点中,括号匹配状态 n == j 的总个数。
算出所有层中,各种括号匹配状态的个数之后,从dp[n][0] (最终符合题目要求的状态) 开始,通过子节点访问父节点
累加奇数层的节点个数。(因为题目的树一定是偶数, 2*n)
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int Maxn = 1e3+10;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9+7;
int dp[Maxn<<1][Maxn<<1];
bool vis[Maxn<<1][Maxn<<1];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(false); cout.tie(false);
memset(vis, false, sizeof(vis));
memset(dp, 0, sizeof(dp));
int n;
cin >> n;
n *= 2;
dp[1][1] = 1;
for(int i = 2; i <= n; ++i) {
for(int j = 0; j <= n; ++j) {
if((j-1 >= 0) && dp[i-1][j-1] > 0) {
dp[i][j] = (dp[i][j]+dp[i-1][j-1])%Mod;
}
if((j+1 <= n) && dp[i-1][j+1] > 0) {
dp[i][j] = (dp[i][j]+dp[i-1][j+1])%Mod;
}
}
}
int ans = 0;
vis[n][0] = true;
for(int i = n; i >= 1; --i) {
for(int j = 0; j <= n; ++j) {
if(vis[i][j]) {
if(j-1 >= 0) vis[i-1][j-1] = true;
if(j+1 <= n) vis[i-1][j+1] = true;
if(i&1) ans = (ans+dp[i][j])%Mod;
}
}
}
cout << ans << endl;
return 0;
}
浙公网安备 33010602011771号