#最大流#洛谷 2764 最小路径覆盖问题

题目

求有向无环图的最小路径覆盖(可以从任意一个点开始)


分析

根据最大流,可得最小路径覆盖=结点数-最大流,所以关键是任意一个点作为起点,所以需要拆点,源点与入点相连,出点与汇点相连,中间就是那些边


代码

#include <cstdio>
#include <queue>
using namespace std;
struct node{int y,w,next;}e[20001];
int n,ans,s,m,t,k=1,ls[301],dis[301],f[301];
int in(){
    int ans=0; char c=getchar();
    while (c<48||c>57) c=getchar();
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans;
}
void add(int x,int y,int w){
    e[++k]=(node){y,w,ls[x]}; ls[x]=k;
    e[++k]=(node){x,0,ls[y]}; ls[y]=k;
}
bool bfs(int s){
    for (int i=1;i<=t;i++) dis[i]=0;
    queue<int>q; q.push(s); dis[s]=1;
    while (q.size()){
        int x=q.front(); q.pop();
        for (int i=ls[x];i;i=e[i].next)
        if (e[i].w>0&&!dis[e[i].y]){
            dis[e[i].y]=dis[x]+1;
            if (e[i].y==t) return 1;
            q.push(e[i].y);
        }
    }
    return 0;
}
int dinic(int x,int now){
    if (x==t||!now) return now;
    int rest=0,h;
    for (int i=ls[x];i;i=e[i].next)
    if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
        rest+=(h=dinic(e[i].y,min(now-rest,e[i].w)));
        e[i].w-=h; e[i^1].w+=h;
        if (now==rest) return rest;
    }
    if (!rest) dis[x]=0;
    return rest;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
void prin(int ans){if (ans>9) prin(ans/10); putchar(ans%10+48);}
void print(int x){
    prin(x); putchar(' ');
    for (int i=ls[x];i;i=e[i].next)
    if (!e[i].w&&e[i].y>n&&e[i].y<s)
    print(e[i].y-n);
}
int main(){
    n=in(); m=in(); t=(s=n<<1|1)+1;
    for (int i=1;i<=n;i++) add(s,i,1),add(i+n,t,1),f[i]=i;//建图
    while (m--) add(in(),in()+n,1);
    while (bfs(s)) ans+=dinic(s,400);
    for (int i=2;i<=k;i++)
    if (e[i].y>n&&e[i].y<s&&e[i^1].y<=n&&!e[i].w){
        int fa=getf(e[i^1].y); int fb=getf(e[i].y-n);//并查集维护 
        int fc=(fa<fb)?fa:fb; f[fc]=fa+fb-fc;//按秩合并+路径压缩
    }
    for (int i=1;i<=n;i++) if (f[i]==i) print(i),putchar('\n'); //如果找到单独的路径就输出
    prin(n-ans);//结点数-最大流
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81431725