【网络流24题】最小路径覆盖问题

11 篇文章 1 订阅
7 篇文章 0 订阅

Description

给定有向图 G=(V,E)。设 P是 G的一个简单路(顶点不相交)的集合。如果 V中每个顶点恰好在 P的一条路上,则称 P是 G的一个路径覆盖。 P中路径可以从 V的任何一个顶点开始,长度也是任意的,特别地,可以为 0。G的最小路径覆盖是 G的所含路径条数最少的路径覆盖。
设计一个有效算法求一个有向无环图 G的最小路径覆盖。
提示:设 V={1,2,…,n},构造网络 G1=(V1,E1)如下:
V1 ={x0, x1,., xn }∪{y0, y1,., yn },
E1 ={(x , x ): i ∈V}∪{( y , y ): i ∈V}∪ {(x , y ):(i. j) ∈ E}
每条边的容量均为 1。求网络 G1的( x0, y0)最大流。

对于给定的给定有向无环图 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<150
m<3000
code

网络流最大流
将图中的每个点拆成两个,分别放入x集合和y集合。
对于一条边(u,v),从x集合中的u到y集合中的v建一条容量为inf的边。
对于x集合中的每个元素,从原点向其建一条容量为1的边。
对于y集合中的每个元素,从其向汇点建一条容量为1的边。
跑一遍最大流,ans=n-最大流。
如果没有匹配,那么说明需要n条路径,每增加一个匹配,则减少一条所需路径覆盖,求最大匹配,即为求最小路径覆盖。

code
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20010
#define inf 0x7fffffff
using namespace std;
struct ss
{
    int next,to,s;
};
ss Edge[N<<1];
int n,m,tot=1,T;
int head[N];
int h[N];
int qq[N],in[N];

void add(int x,int y,int z)
{
    Edge[++tot].next=head[x];
    Edge[tot].to=y;
    Edge[tot].s=z;
    head[x]=tot;
}

void ins(int x,int y,int z)
{
    add(x,y,z),add(y,x,0);
}

bool bfs()
{
    queue<int>Q;
    Q.push(0);
    memset(h,-1,sizeof(h));
    h[0]=0;
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        for(int i=head[u];i;i=Edge[i].next)
        {
            int to=Edge[i].to,s=Edge[i].s;
            if(!s||h[to]!=-1)continue;
            h[to]=h[u]+1;
            Q.push(to);
        }
    }
    if(h[T]==-1)return false;
    return true;
}

int dfs(int u,int f)
{
    if(u==T)return f;
    int used=0;
    for(int i=head[u];i;i=Edge[i].next)
    {
        int to=Edge[i].to,s=Edge[i].s;
        if(!s||h[to]!=h[u]+1)continue;
        int w=f-used;
        w=dfs(to,min(w,s));
        if(w&&u&&to!=T)qq[u]=to-n,in[to-n]++;
        used+=w;
        Edge[i].s-=w;
        Edge[i^1].s+=w;
        if(used==f)return f;
    }
    if(!used)h[u]=-1;
    return used;
}

int dinic()
{
    int ans=0;
    while(bfs())
        ans+=dfs(0,inf);
    return ans;
}

void getans()
{
    for(int i=1;i<=n;i++)
    {
        if(!in[i])
        {
            int x=i;
            printf("%d",x);
            while(qq[x])
            {
                printf(" %d",qq[x]);
                x=qq[x];
            }
            puts("");
        }
    }
}

int main()
{
//  freopen("tt.in","r",stdin);
//  freopen("path3.in", "r", stdin);
//  freopen("path3.out", "w", stdout);
    cin>>n>>m;
    T=(n<<1)+1;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ins(x,y+n,inf);
    }
    for(int i=1;i<=n;i++)
    {
        ins(0,i,1);
        ins(i+n,T,1);
    }
    int tmp=dinic();
    getans();
    cout<<n-tmp<<endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值