zoukankan      html  css  js  c++  java
  • 网络流24题——最小路径覆盖问题

    问题描述:

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


    编程任务:

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


    预备知识:我看描述还是不懂最小路径覆盖是什么东西(太蒟蒻没办法),觉得网上这两张图还是很好理解的

    橙色为一条路径,找完后变成这样

    也就是说,从入度为零的点开始找路径直至找到出度为零的,每找完一条路径,把其路径上的点的所有出边入边都切断,再继续从别的入度为零的点开始,直到覆盖完所有的点,所用的路径数就是最小路径覆盖。


    建模:把一个点变成两个点x,y。假设两个点u,v相连,建图时将u的x向v的y连一条边,源点s向所有的x连边,所有的y向汇点连边,跑一边网络流找到最大匹配,用点数n-最大匹配即为答案。

    可以这么理解:如果无匹配,显然要n条路径才能覆盖所有点,两个点匹配意味着将可以把它们用一条路径覆盖,路径数就可以减1。当然,既然可以用网络流找最大匹配,二分图也可以,码量还会少点。


    dinic最大流做法代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define inf 0x7fffffff
     5 using namespace std;
     6 int n,m,cnt=1,ans,head[10001],q[10001],to[10001],h[10001];
     7 bool mark[10001];
     8 struct node{int to,nex,val;}e[2000001];
     9 void ins(int u,int v,int w)
    10 {
    11     cnt++;
    12     e[cnt].to=v;
    13     e[cnt].val=w;
    14     e[cnt].nex=head[u];
    15     head[u]=cnt;
    16 }
    17 void insert(int u,int v,int w)
    18 {
    19     ins(u,v,w);
    20     ins(v,u,0);
    21 }
    22 bool bfs()
    23 {
    24     memset(h,-1,sizeof(h));
    25     q[0]=h[0]=0;
    26     int l=0,r=1;int u;
    27     while (l<r)
    28     {
    29         u=q[l++];
    30         for (int i=head[u];i;i=e[i].nex)
    31         {
    32             int ne=e[i].to;
    33             if (h[ne]==-1&&e[i].val)
    34             {
    35                 h[ne]=h[u]+1;
    36                 q[r++]=ne;
    37             }
    38         }
    39     }
    40     if (h[8001]==-1) return 0;
    41     return 1;
    42 }
    43 
    44 int dfs(int u,int flow)
    45 {
    46     int used=0,w;
    47     if (u==8001) return flow;
    48     for (int i=head[u];i;i=e[i].nex)
    49     {
    50         int ne=e[i].to;
    51         if (h[ne]==h[u]+1&&e[i].val)
    52         {
    53             w=flow-used;
    54             w=dfs(ne,min(w,e[i].val));
    55             e[i].val-=w,e[i^1].val+=w;
    56             used+=w;
    57             if (w!=0)
    58             {
    59                 to[u]=ne;
    60                 if (e[i].to-n>0) mark[ne-n]=1;
    61             }
    62             if (used==flow) return flow;
    63         }
    64     }
    65     if (!used) h[u]=-1;
    66     return used;
    67 }
    68 void dinic(){while(bfs())ans-=dfs(0,inf);}
    69 int main()
    70 {
    71     scanf("%d%d",&n,&m);
    72     for(int i=1;i<=m;i++)
    73        {
    74           int u,v;
    75           scanf("%d%d",&u,&v);
    76           insert(u,v+n,inf);
    77        }
    78     for(int i=1;i<=n;i++)insert(0,i,1);
    79     for(int i=1;i<=n;i++)insert(i+n,8001,1);
    80     ans=n;
    81     dinic();
    82     for(int i=1;i<=n;i++)
    83     {
    84         if(mark[i])continue;
    85         printf("%d",i);
    86         int k=i;
    87         while(to[k])
    88         {
    89             printf(" %d",to[k]-n);
    90             k=to[k]-n;
    91         }
    92         printf("
    ");
    93     }
    94     printf("%d",ans);
    95     return 0;       
    96 }
    View Code
  • 相关阅读:
    为什么电影里的黑客都不屑用鼠标? (转)
    专注做好一件事(转) focus---这个世界上最成功的人,他们在某一领域获得成功之后,可通过经营杠杆进入任何他们想要涉足的领域。而这都得依赖于他们曾极致的专注在做好一件事情上。
    世间万物都是遵循某种类似的规律,谁先把握了这些规律,谁就最早成为了强者。
    走的时候不要太急,有时间要停下来想一想当初为什么而走,这样,才会走的更稳,走的更明白。
    Android笔记: Android版本号
    Android笔记:真机调试无法输出Log 信息的问题
    阿里云服务器试用
    Android笔记:利用InputStream和BufferedReader 进行字节流 字符流处理
    Android笔记:java 中的数组
    Android笔记:C memory copy
  • 原文地址:https://www.cnblogs.com/71-111/p/9333950.html
Copyright © 2011-2022 走看看