Codeforces1689C - Infected Tree(树形DP)

题目链接: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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
typedef pair 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>g(n+1);
        for(int i=0;i>u>>v;
            g[u].push_back(v);
            g[v].push_back(u);
        }

        vectorsz(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<

你可能感兴趣的:(杂题,深度优先,算法,c++)