2023北航校赛-E 二分图最小点覆盖=n-最大独立集

link:https://codeforces.com/gym/104880
题意:给一棵树,每次操作选一个还存在的点,将点和所有与其相连的边删去。要求 操作次数+剩余边数 的最小值。


复杂度不重要,当做要线性的就好。这题本身的想法很自然:剩余边数=n-1-删去的边数,所以只要每当还存在某个点,有连边的话,多操作一次会让次数+1,而删去边数至少-1,所以最终状态必然是所有边都被删掉。

那现在就变成求一个点集,使得删掉这些点之后就能删去所有边,这就是最小点覆盖!而对于二分图来说(树自然是二分图了),最小点覆盖=点数-最大独立集,直接去dp算最大独立集即可

[!Note]
点覆盖就是用点去覆盖边的点集,类似地边覆盖就是用边覆盖点的边集。
而独立集则是两两之间不连边的点集。
路径覆盖是覆盖所有点的路径集合。
对于二分图来说,这几者间有很深刻的联系:
- 最大流=最小割=最大匹配
- 最小点覆盖=最大匹配=n-最大独立集
- 最小边覆盖=n-最大匹配=n-最小点覆盖=最大独立集
- 最小路径覆盖:拆点,\(V\) 拆成 \(V_x\to V_y\) 后,=n-新图的最大匹配

这里只是顺带回顾一下,这里如果能马上意识到算最大独立集的话,就是一个很简单的树上dp的做法

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=5e5+5;
int n,f[N][2];
vector<vector<int>> G;
void dfs(int x,int fa){
    f[x][0]=0;f[x][1]=1;
    for(auto to:G[x])if(to!=fa){
        dfs(to,x);
        f[x][0]+=max(f[to][0],f[to][1]);
        f[x][1]+=f[to][0];
    }
}    
int main(){
    fastio;
    cin>>n;
    G=vector<vector<int>>(n+1);
    rep(i,1,n-1){
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,-1);
    cout<<n-max(f[1][0],f[1][1]);
    return 0;
}
posted @ 2024-04-05 02:25  yoshinow2001  阅读(10)  评论(0编辑  收藏  举报