P2764 最小路径覆盖问题(网络流24题之一)

题目描述

«问题描述:

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

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

«编程任务:

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

输入输出格式

输入格式:

件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。

输出格式:

从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。

输入输出样例

输入样例#1:

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

输出样例#1:

1 4 7 10 11
2 5 8
3 6 9
3

说明

1<=n<=150,1<=m<=6000

由@zhouyonglong提供SPJ

Solution:

先简单的解释一下最小路径覆盖:大致就是在一个有向无环图中,用最少多少条简单路径能将所有的点覆盖(简单路径简单来说就是一条路径不能和其他路径有重复的点,当然也可以认为单个点是一条简单路径)。

仔细思考,容易发现有些类似于二分图匹配的问题,异曲同工。

算法:把原图的每个点V拆成Vx和Vy两个点,如果有一条有向边A->B,那么就加边Ax−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。

证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。

因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。

方法一:二分图匹配。综合上述所说的,我们可以直接建图,然后跑匈牙利算法,输出的话只需将所匹配的点依次输出就ok了。

代码(copy一份):

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int maxn = 305;
 5 const int maxm = 20005;
 6 struct Edge
 7 {
 8     int v;
 9     Edge *next;
10 }E[maxm], *H[maxn], *edges;
11 int res[maxn];
12 int vis[maxn];
13 void addedges(int u, int v)
14 {
15     edges->v = v;
16     edges->next = H[u];
17     H[u] = edges++;
18 }
19 void init()
20 {
21     edges = E;
22     memset(H, 0, sizeof H);
23     memset(res, -1, sizeof res);
24 }
25 bool find(int u)
26 {
27     for(Edge *e = H[u]; e; e = e->next) if(!vis[e->v]) {
28         int v = e->v;
29         vis[v] = 1;
30         if(res[v] == -1 || find(res[v])) {
31             res[v] = u;
32             return true;
33         }
34     }
35     return false;
36 }
37 int n, m;
38 vector<int> ans;
39 int to[maxn];
40 void work()
41 {
42     int u, v;
43     for(int i = 1; i <= m; i++) {
44         scanf("%d%d", &u, &v);
45         addedges(u, v);
46     }
47     int result = 0;
48     for(int i = 1; i <= n; i++) {
49         memset(vis, 0, sizeof vis);
50         if(find(i)) result++;
51     }
52     memset(to, 0, sizeof to);
53     for(int i = 1; i <= n; i++) if(res[i] != -1) to[res[i]] = i;
54     for(int i = 1; i <= n; i++) if(res[i] == -1) {
55         ans.clear();
56         int u = i;
57         ans.push_back(u);
58         while(to[u]) {
59             u = to[u];
60             ans.push_back(u);
61         }
62         for(int j = 0; j < ans.size(); j++) printf("%d%c", ans[j], j == ans.size() - 1 ? ‘\n‘ : ‘ ‘);
63     }
64     printf("%d\n", n - result);
65 }
66 int main()
67 {
68     scanf("%d%d", &n, &m);
69     init();
70     work();
71     return 0;
72 }  

方法二:网络最大流。这里的做法和二分图匹配用最大流的做法是一样的。附加炒鸡源S和炒鸡汇T,然后建图(边权为1),最后跑最大流,输出时方法很多,我选择的是从汇点按残余流量的有无来往前找一条路径并递归输出。

代码(手打Dinic):

 1 #include<bits/stdc++.h>
 2 #define il inline
 3 using namespace std;
 4 const int N=1000,inf=23333333;
 5 int n,m,s,t=520,h[N],dis[N],cnt=1,fa[N];
 6 struct edge{
 7 int to,net,v;
 8 }e[100005];
 9 il void add(int u,int v,int w)
10 {
11     e[++cnt].to=v,e[cnt].net=h[u],e[cnt].v=w,h[u]=cnt;
12     e[++cnt].to=u,e[cnt].net=h[v],e[cnt].v=0,h[v]=cnt;
13 }
14 queue<int>q;
15 il bool bfs()
16 {
17     memset(dis,-1,sizeof(dis));
18     q.push(s),dis[s]=0;
19     while(!q.empty())
20     {
21         int u=q.front();q.pop();
22         for(int i=h[u];i;i=e[i].net)
23         if(dis[e[i].to]==-1&&e[i].v>0)dis[e[i].to]=dis[u]+1,q.push(e[i].to);
24     }
25     return dis[t]!=-1;
26 }
27 il int dfs(int u,int op)
28 {
29     if(u==t)return op;
30     int flow=0,used=0;
31     for(int i=h[u];i;i=e[i].net)
32     {
33         int v=e[i].to;
34         if(dis[v]==dis[u]+1&&e[i].v)
35         {
36             used=dfs(v,min(op,e[i].v));
37             if(!used)continue;
38             flow+=used,op-=used;
39             e[i].v-=used,e[i^1].v+=used;
40             fa[u]=v;
41             if(!op)break;
42         }
43     }
44     if(!op)dis[u]=-1;
45     return flow;
46 }
47 il void print(int x)
48 {
49     if(x<=s)return;
50     printf("%d ",x);
51     for(int i=h[x];i;i=e[i].net)
52         if(!e[i].v&&e[i].to<=n*2)print(e[i].to-n);
53 }
54 int main()
55 {
56     scanf("%d%d",&n,&m);
57     for(int i=1;i<=n;i++)fa[i]=i;
58     for(int i=1;i<=n;i++)add(s,i,1),add(i+n,t,1);
59     int u,v;
60     for(int i=1;i<=m;i++)
61     {
62         scanf("%d%d",&u,&v);
63         add(u,v+n,1);
64     }
65     int ans=n;
66     while(bfs())ans-=dfs(s,inf);
67     for(int i=h[t];i;i=e[i].net)
68     {
69         if(e[i].v)continue;
70         print(e[i].to-n),printf("\n");
71     }
72     printf("%d",ans);
73     return 0;
74 }

原文地址:https://www.cnblogs.com/five20/p/8157492.html

时间: 2024-08-27 20:27:20

P2764 最小路径覆盖问题(网络流24题之一)的相关文章

P2764 最小路径覆盖问题

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

[loj #6003]「网络流 24 题」魔术球 二分图最小路径覆盖,网络流

#6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 假设有 n nn 根柱子,现要按下述规则在这 n nn 根柱子中依次放入编号为 1,2,3,4,? 1, 2, 3, 4, \cdots1,2,3,4,? 的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何 2 22 个相邻球的编号之和为完全平方数. 试设计一个算法,计算出在

洛谷 P2764 最小路径覆盖问题【匈牙利算法】

经典二分图匹配问题.把每个点拆成两个,对于原图中的每一条边(i,j)连接(i,j+n),最小路径覆盖就是点数n-二分图最大匹配.方案直接顺着匹配dsf.. #include<iostream> #include<cstdio> using namespace std; const int N=505,M=120005; int n,m,h[N],cnt,lk[N],t,v[N],ans; struct qwe { int ne,to; }e[M]; int read() { int

luogu P2764 最小路径覆盖问题

DAG的最小不相交路径覆盖板子 #include<bits/stdc++.h> using namespace std; int ans=0,len=0,lin[500],din[50000],dout[50000],level[500],x,y,S,T,n,m,q[500]; int q1[500],q2[500],tail1=0,tail2=0; bool f[500]; struct one { int x,y,v,next,reverse; }; one e[30000]; void

网络流24题!!!!

网1:飞行员匹配 题目链接:https://www.oj.swust.edu.cn/problem/show/1736 俩种做法: /**********二分图匹配做法***********/ #include<bits/stdc++.h> using namespace std; const int M=102; int n,m; int e[M][M],match[M],used[M]; bool find(int u){ for(int i=1;i<=m;i++){ if(e[u]

LiberOJ #6002. 「网络流 24 题」最小路径覆盖

#6002. 「网络流 24 题」最小路径覆盖 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 给定有向图 G=(V,E) G = (V, E)G=(V,E).设 P PP 是 G GG 的一个简单路(顶点不相交)的集合.如果 V VV 中每个顶点恰好在 P PP 的一条路上,则称 P PP 是 G GG 的一个路径覆盖.P PP 中路径可以从 V VV 的任何一个顶点开始,

LiberOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 假设有 n nn 根柱子,现要按下述规则在这 n nn 根柱子中依次放入编号为 1,2,3,4,? 1, 2, 3, 4, \cdots1,2,3,4,? 的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何 2 22 个相邻球的编号之和为完全平方数. 试设计一个算法,计算出在 

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

题目链接:最小路径覆盖问题 哇 卡在输出也是醉了 重要结论:最小路径覆盖数 = 结点数(拆成边之前) -  最大流 本题也是点拆边 与[网络流24题 #04]魔术球问题 有异曲同工之妙 void output(int x){ if(x >= S) return ; printf("%d ", x >> 1); for(int i = head[x]; i != -1; i = edge[i].next) if(!edge[i].w && edge[i]

网络流24题之 1738: 最小路径覆盖问题

网络流24题之 1738: 最小路径覆盖问题 最小路径覆盖问题 模板题,求一个图的最小路径覆盖,输出边数和,路径.不会输出路径的跑dinic然后把图输出来就懂了. #include <bits/stdc++.h> using namespace std; int k; struct Dinic { static const int MAXN = 30005 + 7; static const int MAXM = 1e7 + 7; static const int INF = 0x3f3f3f