线性规划与网络流 03最小路径覆盖问题
题意:
有向无环图,如何以最少的路径数覆盖图中所有点,要求每个点在且仅在一条路径上。
思路:
定理:一个有向无环图的最小路径覆盖等于最大匹配。
也就是说,求出最大匹配后,用总点数减去最大匹配值即能得到最小路径数。
如何用网络流跑二分匹配?把原来的点拆成两个点,命名为x、y。
即{1,2,3,...,n}->{x1,x2,x3,...,xn}U{y1,y2,y3,...,yn}。
然后设源点S,向每个x连容量为1的边。
设汇点T,每个y向T连容量为1的边。
若某个原始点i与j有有向边(i,j),则转化成(xi,yj)。
然后跑一遍最大流,输出方案时dfs一下即可,详见程序。
源码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
#define inf (1000000000)
const int MAXN = 1000 + 5;
int head[MAXN], cur[MAXN], cnt;
int n, m;
int d[MAXN], p[MAXN], num[MAXN];
queue<int>que;
struct Edge
{
int u, v;
int flow, ne;
Edge(){}
Edge(int _u, int _v, int _flow){u = _u, v = _v, flow = _flow, ne = head[_u];}
}edge[MAXN * MAXN * 2];
void add_edge(int u, int v)
{
edge[cnt] = Edge(u, v, 1);
head[u] = cnt++;
edge[cnt] = Edge(v, u, 0);
head[v] = cnt++;
}
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
int u, v;
for(int i = 1 ; i <= n ; i++)
add_edge(0, i * 2), add_edge(i * 2 + 1, 1);
for(int i = 0 ; i < m ; i++){
scanf("%d%d", &u, &v);
add_edge(u * 2, v * 2 + 1);
}
}
int vis[MAXN];
void BFS(int t)
{
memset(vis, 0, sizeof(vis));
while(!que.empty()) que.pop();
d[t] = 0;
vis[t] = 1;
que.push(t);
while(!que.empty()){
int u = que.front(); que.pop();
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(vis[v] == 0){
vis[v] = 1;
que.push(v);
d[v] = d[u] + 1;
}
}
}
}
int Augment(int s, int t)
{
int ans = inf;
int now = t;
while(now != s){
ans = min(ans, edge[p[now]].flow);
now = edge[p[now]].u;
}
now = t;
while(now != s){
edge[p[now]].flow -= ans;
edge[p[now]^1].flow += ans;
now = edge[p[now]].u;
}
return ans;
}
int ISAP(int s, int t)
{
int flow = 0;
BFS(t);
memset(num, 0, sizeof(num));
for(int i = 0 ; i <= n * 2 ; i++)
cur[i] = head[i], num[d[i]]++;
while(!que.empty()) que.pop();
int u = s;
while(d[s] < n * 2 + 1){
if(u == t){
flow += Augment(s, t);
u = s;
}
int ok = 0;
for(int now = cur[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(edge[now].flow && d[v] == d[u] - 1){
p[v] = now;
cur[u] = now;
u = v;
ok = 1;
break;
}
}
if(!ok){
int tm = n * 2 + 1;
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
if(edge[now].flow) tm = min(tm, d[edge[now].v]);
}
if(--num[d[u]] == 0) break;
num[d[u] = tm + 1]++;
cur[u] = head[u];
if(u != s) u = edge[p[u]].u;
}
}
return flow;
}
int out[MAXN], out_cnt;
void dfs(int u)
{
out[out_cnt++] = u;
vis[u] = 1;
u = 2 * u;
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(vis[v / 2] == 0 && edge[now].flow == 0 && v != 0){
dfs(v / 2);
}
}
}
int main()
{
freopen("path10.in", "r", stdin);
while(scanf("%d%d", &n, &m) != EOF){
init();
int ans = n - ISAP(0, 1);
memset(vis, 0, sizeof(vis));
for(int i = 1 ; i <= n ; i++){
if(vis[i] == 0){
out_cnt = 0;
dfs(i);
int f = 1;
for(int j = 0 ; j < out_cnt ; j++){
if(f) f = 0;
else printf(" ");
printf("%d", out[j]);
}
printf("\n");
}
}
printf("%d\n", ans);
}
return 0;
}