Problem F. Cable Protection (基环树求最小覆盖)

Problem F. Cable Protection (基环树求最小点覆盖)

周赛补提,邻近退役又学到之前没学过的,可能这就是拿不到牌的原因吧。。。寄

树形dp求最小点覆盖

这边先介绍一下最小覆盖,简单来说:对于图G=(V,E),从顶点集V中去尽量少的点组成一个集合,使得边集E中所有的和边取出来都的点相连

同时了解到还有最小支配集,和最大独立集,这边也简单介绍,之后再学
最小支配集:对于图G=(V,E),从V取尽量少的点组成一个集合,使得V中剩余的点都与取出来的点有边相连
最大独立集:对于图G=(V,E),总V中取尽量多的点组成一个集合,使得这写点之间没有边相连

我们把点加入到最小覆盖的操作成为覆盖,之后覆盖都是这个意思。

对于该问题,我们考虑用树形dp来处理,dp[u][0/1],代表这个点是否被取到覆盖的集合中,对于父亲节点u,dp[u][0]表示父节点没有取进覆盖的集合中,所以孩子节点必须要被覆盖,不然父子节点之间必有边;dp[u][1]表示父亲节点被覆盖,这时候对于u的所有孩子v,孩子是否被覆盖都没有关系,因为这条边已经断开了,所以我们考虑贪心,dp[u][1] = Σmin(dp[v][0],dp[v][1])。 转移方程就出来了,最后我们只需要对根节点是否被覆盖的情况取个min,就可以得到答案

这边贴下代码

void dfs(int u,int pre,int root){
    dp[u][0] = dp[u][1] = 0;
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(v==pre||v==root) continue;
        dfs(v,u,root);
        dp[u][0] += dp[v][1];
        dp[u][1] += min(dp[v][1],dp[v][0]);
    }
    dp[u][1]++;  //这节点被覆盖,集合中个数+1
}
//最后对答案取个max,因为答案是从叶子节点递推上来的
ans = min (dp[1][0],dp[1]][1]);

学会了这个之后,我们就可以写周赛这道题了。

首先我们先理解一下什么叫基环树,基环树就是比普通的树多了一条边,n个点n条边,多了一个环

赛中的时候这题是学弟读的,题意大概是给你一个基环树,删掉最少的节点,使得剩下的点都是独立的。现在理解过来,不就是取最少个点,让所有的边都和这些点有边相连吗?

那对于基环树,我们可以考虑环上的一条边,必须断开,才能构成一个棵树,我们断开从1~n的那条边,所有要么是1节点被覆盖,要不就是n节点被覆盖。我们跑两遍求树的最小点覆盖,一次从1节点开始跑,一次从n节点开始跑,最后的答案就是
min(dp[1][1],dp[n][1])

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> P;
const int maxn=1e6+5;
const ll mod=998244353;
//pair<int,int> res[maxn];
//multiset<int> s;
int dp[maxn][2];

int ans,n,m;
vector<int> G[maxn];
void dfs(int u,int pre,int root){ //root是环上的根节点,第一次是1,第二次是n
    dp[u][0] = dp[u][1] = 0;
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(v==pre||v==root) continue;
        dfs(v,u,root);
        dp[u][0] += dp[v][1];
        dp[u][1] += min(dp[v][1],dp[v][0]);
    }
    dp[u][1]++;
}

void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int u,v;
        cin>>u>>v;
        u++,v++;
        G[u].push_back(v),G[v].push_back(u);
    }
    while (m--){
        int u,v;
        cin>>u>>v;
        u++,v++;
        G[u].push_back(v),G[v].push_back(u);
    }
    dfs(1,n,1);
    ans = dp[1][1];
    dfs(n,1,n);
    ans = min(ans,dp[n][1]);
    cout<<ans<<'\n';
}
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    ll T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值