Infected Tree

这篇博客讨论了一个关于树的优化问题:一棵有n个顶点、n-1条边且每个顶点度不超过3的树,根节点被病毒感染。Misha可以删除未感染的节点来阻止病毒传播,目标是拯救最多的边。通过树上动态规划的方法,博主介绍了如何从叶节点向上计算每个节点被感染时能救的最大边数,并给出了具体的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

传送门 

题意:

有n个顶点,n - 1条边,没个顶点的度最多为3,根节点是1,他的度最多为2,不幸的是根节点被感染了,有n次事件会发生,Misha选择一个没有被感染过的点,删了他,删了他相当于删了他所有的边,或者什么都不做,这样之后先前被感染的点将会传染给与他相连的点,求他能够救的做多的边,被删除的边不能算作被救

思路:

很明显这是一棵树,自己想下,就可以想到每次删点肯定删有更多的儿子节点的点,但是这样一层一层不断的要找那个最大的很难实现,这时可以想想,树,能不能从尾部做手脚,尾部的值都是可以计算的,以尾部向上推移,树上dp的思想就可以体现出来了,对于每个点我肯定要知道他的子节点有多少个,这个不难,只要dfs跑一遍即可,算出了子节点的数量,我就可以去观察状态,刚开始只有1节点是感染的,他的子节点是不感染的,这样的情况最多能够救多少子节点,那在这个过程中,我也可以假设一个节点是是感染的,然后看看最多能够救多上个,有三种情况

  • 如果是最后的节点是感染点的话,一个都救不了

  • 一个点是感染点,他的子节点只有一个,那他能救的就是他的子节点数 - 1,因为他的直属子节点是要被删掉的

  • 一个点是感染点,他的子节点有两个,那他就要比较哪个子节点当感染点的代价要小,dp[a] + cnt[b] - 1代表当a当感染点时,能够救的节点,dp[b] + cnt[a] - 1代表当b当感染点时,能够救的节点dp[x] = max(dp[a] + cnt[b] - 1, dp[b] + cnt[a] - 1)

#include <bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false);
using namespace std;

typedef long long ll;
const int N = 3e5 + 10;
int T, n;
vector<int> v[N];
int cnt[N]; //记录每个节点的子节点数,注意这里也包含了他自身
int dp[N];  //dp[i]代表第i个点被感染时,能够就的最多的节点数

void Init()
{
    for (int i = 1; i <= n; ++i)
    {
        v[i].clear();
    }
}

void get_cnt(int x, int fa)
{
    cnt[x] = 1;
    for (int i = 0; i < v[x].size(); ++i)
    {
        int y = v[x][i];
        if (y == fa)
            continue;
        get_cnt(y, x);
        cnt[x] += cnt[y];    
    }
}

void get_dp(int x, int fa)
{
    if (v[x].size() == 1 && x != 1) //说明这个就是最后的节点
    {
        dp[x] = 0;
        return ;
    }
    else if (v[x].size() == 2 && x != 1)    //说明这个是中间的只有一个子节点的点
    {
        for (int i = 0; i < v[x].size(); ++i)
        {
            int y = v[x][i];
            if (y == fa)
                continue;
            dp[x] = cnt[y] - 1; //除了删除的那个点,其他的点都可以救    
        }
    }
    else    //1节点和中间的有两个分支的点
    {
        int a = -1, b = -1;
        for (int i = 0; i < v[x].size(); ++i)
        {
            int y = v[x][i];
            if (y == fa)
                continue;
            if (a == -1)
                a = y;
            else 
                b = y;
            get_dp(y, x);            
        }
        dp[x] = max(dp[a] + cnt[b] - 1, dp[b] + cnt[a] - 1);    //dp思想,还是模拟,中间的状态
    }
}

int main()
{
	//IOS; cin.tie(0), cout.tie(0);
    cin >> T;
    while (T--)
    {
        cin >> n;
        Init();
        for (int i = 1; i <= n - 1; ++i)
        {
            int x, y;
            cin >> x >> y;
            v[x].push_back(y);
            v[y].push_back(x);
        }

        get_cnt(1, -1);

        get_dp(1, -1);

        cout << dp[1] << endl;
    }
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值