• [网络流24题]最小路径覆盖问题


    Description

    给定有向图$G=(V,E)$。设$P$是$G$的一个简单路(顶点不相交)的集合。如果$V$中每个顶点恰好在$P$的一条路上,则称$P$是$G$的一个路径覆盖。$P$中路径可以从$V$的任何一个顶点开始,长度也是任意的,特别地,可以为$0$。$G$的最小路径覆盖是$G$的所含路径条数最少的路径覆盖。
    设计一个有效算法求一个有向无环图$G$的最小路径覆盖。

    Input

    第$1$行有$2$个正整数$n$和$m$。$n$是给定有向无环图$G$的顶点数,$m$是$G$的边数。

    接下来的$m$行,每行有$2$个正整数$i,j$,表示一条有向边$(i,j)$。

    Output

    从第$1$行开始,每行输出一条路径。

    最后一行是最少路径数。

    Sample Input

    11 12
    1 2
    1 3
    1 4
    2 5
    3 6
    4 7
    5 8
    6 9
    7 10
    8 11
    9 11
    10 11

    Sample Output

    1 4 7 10 11
    2 5 8
    3 6 9
    3

    HINT

    $n;leq;150$

    Solution

    对于一个路径覆盖,有如下性质:

    • 每个顶点$i$属于且只属于一个路径。
    • 路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。

    所以我们可以把每个顶点拆成两个顶点,一个是出发点$x_i$,一个是目标点$y_i$,建立二分图模型。该二分图的任何一个匹配方案,都对应了一个路径覆盖方案。如果匹配数为$0$,那么显然路径数=顶点数。每增加一条匹配边,那么路径覆盖数就减少一个,所以路径数=顶点数-匹配数。要想使路径数最少,则应最大化匹配数,所以要求二分图的最大匹配。

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 305
    #define M 46000
    using namespace std;
    struct graph{
        int nxt,to,f;
    }e[M];
    int a[N],g[N],dep[N],n,m,l,s,t,ans,cnt=1;
    bool v[N];
    queue<int> q;
    inline void addedge(int x,int y,int f){
        e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
    } 
    inline void adde(int x,int y,int f){
        addedge(x,y,f);addedge(y,x,0);
    }
    inline bool bfs(int u){
        memset(dep,0,sizeof(dep));
        dep[u]=1;q.push(u);
        while(!q.empty()){
            u=q.front();q.pop();
            for(int i=g[u];i;i=e[i].nxt)
                if(e[i].f>0&&!dep[e[i].to]){
                    q.push(e[i].to);
                    dep[e[i].to]=dep[u]+1;
                }
        }
        return dep[t];
    }
    inline int dfs(int u,int f){
        int ret=0;
        if(u==t) return f;
        for(int i=g[u],d;i&&f;i=e[i].nxt)
            if(e[i].f>0&&dep[e[i].to]>dep[u]){
                d=dfs(e[i].to,min(f,e[i].f));
                e[i].f-=d;e[i^1].f+=d;ret+=d;f-=d;
            }
        return ret;
    }
    inline int dinic(){
        int ret=0;
        while(true){
            if(!bfs(s)) return ret;
            ret+=dfs(s,N);
        }
    }
    inline void find(int u){
        a[++l]=u;
        for(int i=g[u];i;i=e[i].nxt)
            if(!e[i].f&&!v[e[i].to-n]){
                v[e[i].to-n]=true;find(e[i].to-n);
            }
    }
    inline void Aireen(){
        scanf("%d%d",&n,&m);
        for(int i=1,j,k;i<=m;++i){
            scanf("%d%d",&j,&k);
            adde(j,k+n,1);
        }
        s=(n<<1)+1;t=s+1;
        for(int i=n;i;--i)
            adde(s,i,1);
        for(int i=1;i<=n;++i)
            adde(i+n,t,1);
        ans=n-dinic();
        v[s]=v[t]=true;
        for(int i=1;i<=n;++i)
            if(!v[i]){
                l=0;v[i]=true;find(i);
                for(int j=1;j<=l;++j)
                    printf("%d ",a[j]);
                printf("
    ");
            }
        printf("%d
    ",ans);
    }
    int main(){
        freopen("path.in","r",stdin);
        freopen("path.out","w",stdout);
        Aireen();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
  • 相关阅读:
    hbase安装(zookeeper等)
    虚函数表以及RTTI阅读(阅后焚)
    void及void指针含义的深刻解析
    泛型和模版的区别
    C++析构函数为什么要为虚函数
    C++对象模型
    在C++中子类继承和调用父类的构造函数方法
    什么是复合类型
    C++ read
    gdb 调试c/c++的一些小技巧
  • 原文地址:https://www.cnblogs.com/AireenYe/p/6240784.html
Copyright © 2020-2023  润新知