---
tags: CSES題解
---
CSES Problem Set — Tree Matching 題解
===
題目
---
給一顆有$n$個點的樹。
定義匹配是對的集合,其中每個節點最多是其中一對的端點。匹配中對的最大數量是多少?
### 輸入
第一行輸入包含一個整數$n$ : 節點的數量。這些節點編號為$1,2,\ldots,n$。
接下來有$n-1$描述邊。每一行包含兩個整數$a$和$b$ : 點$a$和$b$間有一條邊。
### 輸出
輸出匹配中最多的對的數量
想法 : 樹形dp
---
由於樹上無環,我們可以透過子節點上的資訊轉移到父節點,根據題意如果父節點與子節點為一對,子節點就不能跟其子節點一對。
我們可以定義狀態$dp(x,0)$表示x節點不與其子節點成對的最大匹配對數,$dp(x,1)$表示x節點與子節點成對的最大匹配對數。
其中$dp(x,0)$可由其子節點的0狀態和1狀態取大值轉移,因為不與子節點成對時,子節點就不會影響父節點的匹配關係。
而$dp(x,1)$則要處理完整顆子樹後,再枚舉每個子節點與他成對取最大值,轉移方式見範例程式碼。
範例程式碼
---
```cpp=
#include<bits/stdc++.h>
#define int long long
using namespace std;
int cnt=1;
vector<int> Head(200500,0);
vector<int> Next(400500);
vector<int> To(400500);
vector<vector<int>> dp(200500,vector<int>(2,0));
void addEdge(int u,int v){
Next[cnt]=Head[u];
To[cnt]=v;
Head[u]=cnt++;
}
/*************鍊式前向星建圖****************/
void dfs(int u,int par){
for(int e=Head[u];e;e=Next[e]){
int v=To[e];
if(v!=par){
dfs(v,u);
//子節點不影響父節點成對
//所以取大者轉移
dp[u][0]+=max(dp[v][1],dp[v][0]);
}
}
//枚舉每個子節點取最大
for(int e=Head[u];e;e=Next[e]){
int v=To[e];
if(v!=par){
//由於dp[u][0]可能由dp[v][1]或dp[v][0]轉移
//我們不能確定從哪個轉移
//所以減去max(dp[v][1],dp[v][0])
//再加上dp[v][0],因為若與子節點成對,子節點不能和其子節點成對
//最後加上1,和子節點成對了,所以對數+1
dp[u][1]=max(dp[u][1],dp[u][0]-max(dp[v][1],dp[v][0])+dp[v][0]+1);
}
}
}
signed main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
addEdge(a,b);
addEdge(b,a);
}
dfs(1,1);
cout<<max(dp[1][0],dp[1][1]);
return 0;
}