题目链接:https://codeforces.com/problemset/problem/1689/C
题意:给定一棵树,除根节点的度数最多为2,其他节点的度数最多为3。有一个病毒从根节点开始向下蔓延,每次病毒会向其下一层的所有子节点蔓延,同时每次我们可以选择删除一个节点,那么以该节点为根的所有子节点就被救下来不会被感染了(不包括删去的节点)。问最多能救下来多少节点。
思路:简单的树形DP,我们可以用
D
P
[
u
]
DP[u]
DP[u]表示以点u被感染,能救下来的子节点的个数。因为除根节点外的其他节点的度数最多为3,那么每个节点最多有2个子节点,对于每个节点我们每次会选取其中一个子节点删除,用
s
z
[
u
]
sz[u]
sz[u]表示以点u为根的子节点的个数,
s
1
,
s
2
s1,s2
s1,s2表示点u的两个子节点,那么删除
s
1
s1
s1的答案就是
s
z
[
s
1
]
?
1
+
d
p
[
s
2
]
sz[s1]-1+dp[s2]
sz[s1]?1+dp[s2],如果删除
s
2
s2
s2,那么答案就是
s
z
[
s
2
]
?
1
+
d
p
[
s
1
]
sz[s2]-1+dp[s1]
sz[s2]?1+dp[s1],还要特判一下对于每个节点只有一个儿子和没有儿子的情况,具体看代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10,INF=1e9;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
int n;cin>>n;
vector<vector<int>>g(n+1);
for(int i=0;i<n-1;i++)
{
int u,v;cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int>sz(n+1,0),dp(n+1,0);
auto dfs = [&](auto&& dfs,int u,int fa)->int
{
sz[u]=1;
int s1=-1,s2=-1;//表示点u的两个子节点
for(auto j:g[u])
{
if(j==fa)continue;
dfs(dfs,j,u);
sz[u]+=sz[j];//统计以点u为根节点的子节点个数
if(s1==-1)s1=j;
else if(s2==-1)s2=j;
}
if(s1==-1)return dp[u]=0;//特判没有子节点的情况
if(s2==-1)return dp[u]=sz[s1]-1;//特判只有一个子节点的情况
return dp[u]=max(sz[s1]-1+dp[s2],sz[s2]-1+dp[s1]);//有两个子节点的情况
};
cout<<dfs(dfs,1,-1)<<"\n";;
}
return 0;
}