Codeforces Round #554 (Div. 2) D. Neko and Aki's Prank(記憶化DFS)

題目鏈接:

D. Neko and Aki's Prank

 

題意:

有一顆二叉樹,每條邊表示 "(" 或 ")" ,在 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章