zoukankan      html  css  js  c++  java
  • [网络流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;
    }
  • 相关阅读:
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    SVN分支
    SVN分支
    SVN 版本回退
    SVN 版本回退
    如何在excel中取消合并单元格后内容自动填充?
    如何在excel中取消合并单元格后内容自动填充?
    如何让自己像打王者荣耀一样发了疯、拼了命的学习?
  • 原文地址:https://www.cnblogs.com/AireenYe/p/6240784.html
Copyright © 2011-2022 走看看