网络流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("\n");
93     }
94     printf("%d",ans);
95     return 0;       
96 }
View Code

猜你喜欢

转载自www.cnblogs.com/71-111/p/9333950.html