LOJ6002—最小路径覆盖

传送门

一道比较友好的网络流?的题了吧

把每个点看做leftileft_i,rightiright_i两个点

如果两个点之间有一条有向边

那就把left向right连一条边

然后做二分图匹配

答案就是总点数减去匹配数

但关键是如何证明这样是正确的

显然最开始的时候每个点是一个独立的集合

每次我们连一条有向边

就相当于把两个点匹配在一起,也就把集合数减少了一个了

那最后集合数就是答案了

#include<bits/stdc++.h>
using namespace std;
int adj[606],nxt[10005],to[10005],lf[10005],rt[10005],vis[10005],ans[10005],cnt,ecnt,n,m;
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
inline void addedge(int u,int v){
    nxt[++cnt]=adj[u];adj[u]=cnt,to[cnt]=v;
}
inline int dfs(int u){
    for(int e=adj[u];e;e=nxt[e]){
        int v=to[e];
        if(vis[v]) continue;
        vis[v]=1;
        if(lf[v]==-1||dfs(lf[v])){
            lf[v]=u,rt[u]=v;
            return true;
        }
    }
    return false;
}
int main(){
    memset(lf,-1,sizeof(lf));
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        addedge(u+n,v);
    }
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        dfs(i+n);
    }
    for(int i=1;i<=n;i++){
        if(lf[i]==-1){
            ecnt++;
            cout<<i<<" ";
            int u=i+n;
            while(rt[u]){
                cout<<rt[u]<<" ";
                u=n+rt[u];
            }
            cout<<'\n';
        }
    }        
    cout<<ecnt<<'\n';
}
posted @ 2018-10-11 21:59  Stargazer_cykoi  阅读(81)  评论(0编辑  收藏  举报