---恢复内容开始---
题面:
有一个n个节点n-1条边组成的树。
每个点看成一个人,连接u和v的边看成是“中意关系”,即u和v两个人都想和对方组队。每个人希望组队的对象有可能有多个。
一支队伍由且仅由两个人组成,并且如果u和v组队了,那么u、v将不能和其他人再组成一支队。
现在问你,这n个人最多能组成多少支队伍。(允许某些人组不了队)
Input
第一行输入一个整数n,m(1<=n<=200000)
接下来n-1行,每行两个整数u,v,表示u和v两个人都想和对方组队。
数据保证是一个合法的树。
Output
输出一个整数,表示最多能组成多少支队伍。
Input
5
1 2
1 3
2 4
4 5
Output
2
大致思路:
首先看到题面,其实很容易想到是一道树形DP。但树上的关系在比赛时间里没有想明白,导致没有做出来。
对于每一个节点,都有取或者不取两个可能。分别用dp[k][1]和dp[k][0]来表示
然后就是节点之间的关系了,对于每一个节点来说,若不取这个点,则肯定是对于每一个子节点的取或者不取求一个max
也就是 dp[k][0]+=max(dp[ v ] [ 1 ] , dp [ v ] [ 0 ] ) v是k的子节点
但对于dp[k][1]就有点麻烦了,因为如果要让dp[k][1]成立,则必然有至少加上一个子节点不取的情况。
1 void tree_dp(int k,int last) 2 { 3 int v; 4 bool flag=false; 5 for(int i=0;i<g[k].size();++i){ 6 v=g[k][i]; 7 if(v==last) 8 continue; 9 tree_dp(v,k); 10 if(dp[v][1]>dp[v][0])//对每一个dp[v]进行选择,如果取的情况值较大,就直接加到dp[v][0]上
11 dp[k][0]+=dp[v][1]; 12 else{//否则就表示第k个节点是可以取到的,同时标记flag。 13 flag=true; 14 dp[k][0]+=dp[v][0];//这里依然加给dp[v][0]是因为在最后会加给dp[k][1],而且第k个点最多只能取一次
15 } 16 } 17 dp[k][1]=dp[k][0];//如果第k个取不到,直接赋值给dp[k][1],对下一层决策无影响 18 if(flag) 19 dp[k][1]++; 20 }
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e5+7; 4 int dp[maxn][2],ans=0; 5 vector<int> g[maxn]; 6 void tree_dp(int k,int last) 7 { 8 int v; 9 bool flag=false; 10 for(int i=0;i<g[k].size();++i){ 11 v=g[k][i]; 12 if(v==last) 13 continue; 14 tree_dp(v,k); 15 if(dp[v][1]>dp[v][0]) 16 dp[k][0]+=dp[v][1]; 17 else{ 18 flag=true; 19 dp[k][0]+=dp[v][0]; 20 } 21 } 22 dp[k][1]=dp[k][0]; 23 if(flag) 24 dp[k][1]++; 25 } 26 int main() 27 { 28 ios::sync_with_stdio(false); 29 //freopen("in.txt","r",stdin); 30 //freopen("ou1.txt","w",stdout); 31 memset(dp,0,sizeof(dp)); 32 int n,u,v; 33 cin>>n; 34 for(int i=0;i<n-1;++i){ 35 cin>>u>>v; 36 g[u].push_back(v); 37 g[v].push_back(u); 38 } 39 tree_dp(1,1); 40 //for(int i=1;i<=n;++i) 41 // cout<<i<<" "<<dp[i][1]<<" "<<dp[i][0]<<endl; 42 ans=max(dp[1][0],dp[1][1]); 43 cout<<ans<<endl; 44 return 0; 45 }
---恢复内容结束---