洛谷 P2764 最小路径覆盖问题【最大流+拆点+路径输出】

时间:2023-03-08 20:39:18

题目链接:https://www.luogu.org/problemnew/show/P2764

题目描述

«问题描述:

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

洛谷 P2764 最小路径覆盖问题【最大流+拆点+路径输出】

每条边的容量均为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

 

题解:

  最小路径覆盖问题。答案就是N-最大二分匹配(证明略,感觉hihoCoder上讲的很详细,推荐看)。

  将每个点拆点分成AB两部分,做最大二分匹配。然后源点到A部的点连边,边权为1;B部点到汇点连边,边权为1,跑最大流......

  关于路径输出问题,可以从汇点开始找残余容量为0的点作为起始点递归输出路径...

代码:

 #include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int N = *+;
const int M = *+;
const int inf = 1e9;
int n, m, S, T;
int dep[N], cur[N];
int head[N];
struct Edge{
int v, c, nex;
Edge(int _v=,int _c=,int _nex=):v(_v),c(_c),nex(_nex){}
}E[M]; int cnt;
void add(int u, int v, int c){
E[cnt].v = v;
E[cnt].c = c;
E[cnt].nex = head[u];
head[u] = cnt++;
} bool bfs() {
queue<int> q;
memset(dep, -, sizeof(dep));
q.push(S); dep[S] = ;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; ~i; i = E[i].nex) {
int v = E[i].v;
if(E[i].c && dep[v] == -) {
dep[v] = dep[u] + ;
q.push(v);
}
}
}
return dep[T] != -;
}
int dfs(int u, int flow) {
if(u == T) return flow;
int w, used=;
for(int i = head[u]; ~i; i = E[i].nex) {
int v = E[i].v;
if(dep[v] == dep[u] + ) {
w = flow - used;
w = dfs(v, min(w, E[i].c));
E[i].c -= w; E[i^].c += w;
if(v) cur[u] = i;
used += w;
if(used == flow) return flow;
}
}
if(!used) dep[u] = -;
return used;
}
int dinic() {
int ans = ;
while(bfs()) {
for(int i = ; i <= T;i++)
cur[i] = head[i];
ans += dfs(S, inf);
}
return ans;
}
void print(int x, int &f) {
if(x <= S) return;
if(f == ) f = ;
else printf(" ");
printf("%d", x); for(int i = head[x]; ~i; i = E[i].nex) {
if(!E[i].c){
print(E[i].v - n, f);
}
}
}
int main() {
int i, j, u, v;
scanf("%d%d", &n, &m);
memset(head, -, sizeof(head));
cnt = ;
S = ; T = *n+;
for(i = ; i < m; ++i) {
scanf("%d%d", &u, &v);
add(u, v+n, ); add(v+n, u, );
}
for(i = ; i <= n; ++i) add(S,i,),add(i,S,);
for(i = ; i <= n; ++i) add(i+n,T,),add(T,i+n,); int ans = dinic(); for(i = head[T]; ~i; i = E[i].nex) {
if(!E[i].c) {
int f = ;
print(E[i].v - n, f);
puts("");
}
}
printf("%d\n", n-ans);
return ;
} /*
7 7
1 2
1 3
2 4
3 4
4 5
4 6
5 7
*/