网络流二十四题之P2764 最小路径覆盖问题

时间:2021-01-08 05:46:14

题目描述

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

提示:设 V=\{1,2,...,n\}V={1,2,...,n} ,构造网络 G_1=\{V_1,E_1\}G1​={V1​,E1​} 如下:

V_1=\{x_0,x_1,...,x_n\}\cup\{y_0,y_1,...,y_n\}V1​={x0​,x1​,...,xn​}∪{y0​,y1​,...,yn​}

E_1=\{(x_0,x_i):i\in V\}\cup\{(y_i,y_0):i\in V\}\cup\{(x_i,y_j):(i,j)\in E\}E1​={(x0​,xi​):i∈V}∪{(yi​,y0​):i∈V}∪{(xi​,yj​):(i,j)∈E}

每条边的容量均为 11 ,求网络 G_1G1​ 的 (x_0,y_0)(x0​,y0​) 最大流。

输入输出格式

输入格式:

第一行有 22 个正整数 nn 和 mm 。 nn 是给定\text{GAP}GAP(有向无环图) GG 的顶点数, mm 是 GG 的边数。接下来的 mm行,每行有两个正整数 ii 和 jj 表示一条有向边 (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\leq n\leq 150,1\leq m\leq 60001≤n≤150,1≤m≤6000

由@FlierKing提供SPJ

这个题目有点难读,我读了挺久的,这个题目是让你找最少的路径使得所有的路径合在一起可以覆盖了整个图。

没有最少自然可以是n,就是所有点自成一条路径。

拆分题目有点难写,我开始根本就没有什么思路,后来看到二分图匹配有一个关于这个的定理,

DAG图的最小路径覆盖数 = 节点数 - 二分图的最大匹配

所以这个就可以求解,然后这个转化成二分图,就需要把一个点拆成两个点,然后再合并。

说多了也没用,自己看代码理解吧。

我开始看题解,以为只能用链式前向星建图,真的要哭了,不太会用,后来发现也可以用vector

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = ;
int level[maxn], iter[maxn];
int head[maxn];
int n,m,s = , t = ;
struct node
{
int to, cap,flow;
int nex;
}exa[maxn<<];
int cnt = ;
void add(int u,int v,int c)
{
exa[++cnt].to = v;
exa[cnt].cap = c;
exa[cnt].flow = ;
exa[cnt].nex = head[u];
head[u] = cnt;
//printf("%d %d %d %d %d\n",cnt,u, exa[cnt].to, exa[cnt].cap, exa[cnt].nex); exa[++cnt].to = u;
exa[cnt].cap = ;
exa[cnt].flow = ;
exa[cnt].nex = head[v];
head[v] = cnt;
//printf("%d %d %d %d %d\n",cnt,v, exa[cnt].to, exa[cnt].cap, exa[cnt].nex);
} void bfs(int s)
{
queue<int>que;
que.push(s);
memset(level, -, sizeof(level));
level[s] = ;
while(!que.empty())
{
int u = que.front(); que.pop();
//printf("%d %d %d\n", u,head[u],exa[head[u]].nex);
for(int i=head[u];i!=-;i=exa[i].nex)
{
node &now = exa[i];
//printf("%d %d %d\n", i, exa[i].to, exa[i].nex);
if(level[now.to]<&&now.cap>now.flow)
{
level[now.to] = level[u] + ;
que.push(now.to);
}
}
}
}
int tag[maxn], to[maxn]; int dfs(int u,int v,int f)
{
if (u == v) return f;
for(int &i=iter[u];i!=-;i=exa[i].nex)
{
node &now = exa[i];
if(now.cap>now.flow&&level[now.to]>level[u])
{
int d = dfs(now.to, v, min(f, now.cap - now.flow));
if(d>)
{
to[u] = now.to;
if (u != s) tag[now.to - n] = ;
now.flow += d;
exa[i ^ ].flow -= d;
return d;
}
}
}
return ;
}
void init()
{
memset(head, -, sizeof(head));
}
int dinic(int s,int t)
{
int flow = ;
while()
{
bfs(s);
if (level[t] < ) break;
for (int i = s; i <= t; i++) iter[i] = head[i];
int f;
while ((f = dfs(s, t, inf)) > ) flow += f; }
for(int i=;i<=n;i++)
{
if(!tag[i])
{
int now = i;
printf("%d ", now);
while(to[now]&&to[now]!=t)
{
printf("%d ", to[now] - n);
now = to[now] - n;
}
printf("\n");
}
}
return flow;
} int main()
{
init();
cin >> n >> m;
t = * n + ;
for (int i = ; i <= n; i++) add(s, i, );
for (int i = ; i <= n; i++) add(i+n, t, );
for(int i=;i<=m;i++)
{
int a, b;
cin >> a >> b;
add(a, b+n, );
}
int ans = dinic(s, t);
printf("%d\n",n - ans);
return ;
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = ;
int level[maxn], iter[maxn];
int n,m,s = , t = ;
struct node
{
int from,to, cap,flow;
node(int from=,int to=,int cap=,int flow=):from(from),to(to),cap(cap),flow(flow){} };
vector<node>e;
vector<int>G[maxn];
void add(int u,int v,int c)
{
e.push_back(node(u, v, c, ));
e.push_back(node(v, u, , ));
int len = e.size();
G[u].push_back(len - );
G[v].push_back(len - );
} void bfs(int s)
{
queue<int>que;
que.push(s);
memset(level, -, sizeof(level));
level[s] = ;
while(!que.empty())
{
int u = que.front(); que.pop();
for(int i=;i<G[u].size();i++)
{
node &now = e[G[u][i]];
if(level[now.to]<&&now.cap>now.flow)
{
level[now.to] = level[u] + ;
que.push(now.to);
}
}
}
}
int tag[maxn], to[maxn]; int dfs(int u,int v,int f)
{
if (u == v) return f;
for(int &i=iter[u];i<G[u].size();i++)
{
node &now = e[G[u][i]];
if(now.cap>now.flow&&level[now.to]>level[u])
{
int d = dfs(now.to, v, min(f, now.cap - now.flow));
if(d>)
{
to[u] = now.to;
// printf("%d %d\n", u, to[u]);
if (u != s) tag[now.to - n] = ;
now.flow += d;
e[G[u][i] ^ ].flow -= d;
return d;
}
}
}
return ;
}
void init()
{
for (int i = ; i <= n + ; i++) G[i].clear();
e.clear();
}
int dinic(int s,int t)
{
int flow = ;
while()
{
bfs(s);
if (level[t] < ) break;
memset(iter, , sizeof(iter));
int f;
while ((f = dfs(s, t, inf)) > ) flow += f; }
//cout << endl;
for(int i=;i<=n;i++)
{
if(!tag[i])
{
int now = i;
printf("%d ", now);
while(to[now]&&to[now]!=t)
{
printf("%d ", to[now] - n);
now = to[now] - n;
}
printf("\n");
}
}
return flow;
} int main()
{
init();
cin >> n >> m;
t = * n + ;
for (int i = ; i <= n; i++) add(s, i, );
for (int i = ; i <= n; i++) add(i+n, t, );
for(int i=;i<=m;i++)
{
int a, b;
cin >> a >> b;
add(a, b+n, );
}
int ans = dinic(s, t);
printf("%d\n",n - ans);
return ;
}