2023北航校賽-E 二分圖最小點覆蓋=n-最大獨立集

yoshinow2001發表於2024-04-05

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

相關文章