题目链接:最小路径覆盖
【线性规划与网络流24题 3】最小路径覆盖
Description
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。
提示:设V={1,2,... ,n},构造网络 G1=(V1,E1)如下:
V1 = { x0, x1, ..., xn} U { y0, y1, ..., yn} ,
E1 = {(x0,xi):i包含于V} U {(yi,y0):i包含于V} U {(xi,yj):(i,j)包含于E}
每条边的容量均为1。求网络G1 的(x0,y0 )最大流。
对于给定的给定有向无环图G,编程找出 G的一个最小路径覆盖。
Input
第1 行有 2个正整数 n和 m。n是给定有向无环图(0<n<=150,0<=m<=n*n)
G 的顶点数, m是G 的边数。 接下来的 m行, 每行有 2 个正整数 i和 j, 表示一条有向边(i,j)。
Output
/*
程序运行结束时,将最小路径覆盖输出。
从第 1 行开始,每行输出一条路径。
文件的最后一行是最少路径数
按字典序输出路径。
*/
一行,包含一个整数,表示最少路径数
Sample Input
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
Sample Output
/*
1 4 7 10 11
2 5 8
3 6 9
*/
3
要求的是最少的路径条数
在点数一定的情况之下,要是希望路径条数越少,那么匹配数肯定越多
所以:我们需要建图跑出最大流
构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi。对于原图中存在的每条边(i,j),在二分图中连接边(Xi,Yj)。
然后把二分图最大匹配模型转化为网络流模型,求网络最大流
最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数
如何求匹配方案呢?
还是一样的:dfs!
因为每条边要么能走,要么不能走,其边上的flow值是固定的(因为容量为1)
根据dfs一条路走到黑,就是某一条路径
用vis数组标记一下,让每个点都访问一次即可
//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
const int maxn=1100;
const int maxm=1200010;
const int INF=999999;
int s,t,tot,n,m;
struct Edge{
int to,nxt,cap,flow;
}edge[maxm];
int tol,Head[maxn];
bool flag[maxn];
int path[maxn],num;
void init(){
memset(Head,-1,sizeof(Head));
tol=2;
}
void addedge(int u,int v,int w,int rw=0){
edge[tol].to=v;
edge[tol].cap=w;
edge[tol].flow=0;
edge[tol].nxt=Head[u];
Head[u]=tol++;
edge[tol].to=u;
edge[tol].cap=rw;
edge[tol].flow=0;
edge[tol].nxt=Head[v];
Head[v]=tol++;
}
int Q[maxn],dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
int front=0,tail=0;
memset(dep,-1,sizeof(dep[0])*(n+1));
dep[s]=0;
Q[tail++]=s;
while(front<tail){
int u=Q[front++];
for(int i=Head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if (edge[i].cap>edge[i].flow&&dep[v]==-1){
dep[v]=dep[u]+1;
if (v==t) return true;
Q[tail++]=v;
}
}
}
return false;
}
int dinic(int s,int t,int n){
int maxflow=0;
while(bfs(s,t,n)){
for(int i=0;i<n;i++) cur[i]=Head[i];
int u=s,tail=0;
while(cur[s]!=-1){
if (u==t){
int tp=INF;
for(int i=tail-1;i>=0;i--)
tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
maxflow+=tp;
for(int i=tail-1;i>=0;i--){
edge[sta[i]].flow+=tp;
edge[sta[i]^1].flow-=tp;
if (edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
}
u=edge[sta[tail]^1].to;
}
else if (cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
sta[tail++]=cur[u];
u=edge[cur[u]].to;
}
else{
while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
cur[u]=edge[cur[u]].nxt;
}
}
}
return maxflow;
}
void getpath(int u){
if (u>2*n) return;
flag[u]=1;
num++;
path[num]=u;
for(int i=Head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if (!flag[v]&&v&&edge[i].flow)
getpath(v-n);
}
}
int main(){
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//freopen("path3.in","r",stdin);
//freopen("path3.out","w",stdout);
init();
int u,v;
scanf("%d%d",&n,&m);
s=0;
t=n+n+1;
tot=n+n+2;
while(m--){
scanf("%d%d",&u,&v);
//maze[u][v+n]=1;
addedge(u,v+n,1);
}
for(int i=1;i<=n;i++){
//maze[s][i]=1;
addedge(s,i,1);
//maze[i+n][t]=1;
addedge(i+n,t,1);
}
int maxflow=dinic(s,t,tot);
/*
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;i++)
if (!flag[i]){
num=0;
getpath(i);
for(int j=1;j<=num;j++)
printf("%d%c",path[j],j==num?'\n':' ');
}
*/
printf("%d\n",n-maxflow);
return 0;
}