題目鏈接:
題意:
有一顆二叉樹,每條邊表示 "(" 或 ")" ,在 n 對括號匹配的合法序列組成的二叉樹中任選幾條邊,選擇的任意兩條邊不能有公共頂點,求可選邊的最大數量。
思路:
記憶化dfs:設 dp[L][R] 表示還剩餘 L 個 "(" ,R 個 ")" 時的答案數。
假設要求 n ,我們從 1 開始遍歷到 n ,每次 dfs 時,節點若有右孩子,那麼右孩子所在子樹一定在之前被計算過了,直接返回dp值。因此一次dfs恰好完整遍歷一條鏈,複雜度爲O(2(1+2+3+...+n)) = O(n^2) 。
答案計算:我們給每個節點一個狀態值,要麼0,要麼1,其中根節點爲0,設節點 i 狀態值爲now,那麼其子節點狀態值爲now^1,以此類推,當某節點狀態爲 1 時表示將其與其父節點相連的邊計入答案。(因爲其子節點狀態爲0,一定不會有和它相連的邊被記入答案,所以能保證一個點只被覆蓋 1 次)
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int n;
ll dp[1024][1024];
ll dfs(int l, int r, int now)
{
if (dp[l][r] != -1) return dp[l][r];
ll ans = 0;
if (l >= 1 && (l - 1 <= r)) {
ans += dfs(l - 1, r, now ^ 1);
ans %= mod;
}
if (r >= 1 && (l <= r - 1)) {
ans += dfs(l, r - 1, now ^ 1);
ans %= mod;
}
if (now) ans = (ans + 1) % mod;
dp[l][r] = ans;
return ans;
}
int main()
{
scanf("%d", &n);
memset(dp, -1, sizeof(dp));
for (int i = 1; i <= n; i++) {
dfs(i, i, 0);
}
printf("%lld\n", dp[n][n]);
return 0;
}