题目链接

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;
}