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;
}