最小路径覆盖问题

问题描述

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

                   V 1 ={ x0,x1,,xn } { y0,y1,,yn },
             E 1 ={ (x0,xi):iV } { yi,y0:iV } { (xi,yj):(i.j)E }

    每条边的容量均为1。求网络G1 的(x0,y0)最大流。

编程任务

    对于给定的给定有向无环图G,编程找出 G的一个最小路径覆盖。

数据输入

    由文件input.txt提供输入数据。文件第1 行有 2个正整数 n和 m。n是给定有向无环图G 的顶点数, m是G 的边数。 接下来的 m行, 每行有 2 个
正整数 i和 j, 表示一条有向边(i,j)。

结果输出

    程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出

一条路径。文件的最后一行是最少路径数。

输入文件示例

input.txt 
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 

输出文件示例

output.txt 
1 4 7 10 11 
2 5 8 
3 6 9 
3 

题解

变态的一道网络流题目,欺骗我感情
好吧,这是一道DAG最短路径覆盖问题。将原来的点进行拆点,对于原来的边(x,y),在网络流图中连接( x0,y1 ),形成一个二分图,再进行dinic,用节点数n减去所得结果就是路径条数。
为了输出方案,我们可以定义一个数组next,对于每一个节点,我们在dfs的时候记录它的下一个节点,存储到next数组中,最终输出的时候用while循环即可。
到网上膜了一圈,感觉黄学长写得非常清楚,值得参考一下。

CODE:

#include<cstdio>
#include<cstring>
const int INF=1e9;
struct queue
{
    int h,t;
    int a[500];
    inline void clear(){h=1,t=0;}
    inline void push(int n){a[++t]=n;}
    inline int front(){return a[h];}
    inline void pop(){h++;}
    inline bool empty(){return h>t;}
}q;
struct edge
{
    int next,to,remain;
}a[20000];
int head[500];
int deep[500];
int next[500];
int n,m,S,T,num=1,ans,x,y;
inline int min(int a,int b){return a<b?a:b;}
inline void add(int x,int y,int cap)
{
    a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num;
    a[++num].next=head[y],a[num].to=x,head[y]=num;
}
inline bool bfs()
{
    memset(deep,0x3f,sizeof(deep));
    q.clear(),q.push(S);
    deep[S]=0;
    while(!q.empty())
    {
        int tmp=q.front();q.pop();
        for(int i=head[tmp];i;i=a[i].next)
          if(deep[a[i].to]>INF&&a[i].remain)
            q.push(a[i].to),deep[a[i].to]=deep[tmp]+1;
    }
    return deep[T]<INF;
}
int dfs(int now,int limit)
{
    if(now==T||!limit) return limit;
    int flow=0,f;
    for(int i=head[now];i;i=a[i].next)
      if(a[i].remain&&deep[a[i].to]==deep[now]+1&&(f=dfs(a[i].to,min(limit,a[i].remain))))
      {
        next[now]=a[i].to;
        flow+=f,limit-=f,a[i].remain-=f,a[i^1].remain+=f;
        if(!limit) return flow;
      }
    deep[now]=-1;
    return flow;
}
inline int dinic()
{
    int ans=0;
    while(bfs()) ans+=dfs(S,INF);
    return ans;
}
void print(int now)
{
    while(next[now])
    {
        printf("%d ",now);
        now=next[now]-n;
    }
    printf("%d\n",now);
}
int main()
{
    scanf("%d%d",&n,&m);
    //1~n:顶点 n+1~2*n:拆点 2*n+1:源点 2*n+2:汇点 
    for(int i=1;i<=m;i++)
      scanf("%d%d",&x,&y),add(x,y+n,1);
    S=n<<1|1,T=S+1;
    for(int i=1;i<=n;i++)
      add(S,i,1);
    for(int i=1;i<=n;i++)
      add(n+i,T,1);
    ans=dinic();
    for(int i=1;i<=n;i++)
      if(!next[n+i]) print(i);
    printf("%d",n-ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值