[网络流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("\n");
        }
    printf("%d\n",ans);
}
int main(){
    freopen("path.in","r",stdin);
    freopen("path.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2017-01-01 10:53  Aireen_Ye  阅读(131)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.