最小路径覆盖问题(网络流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 行开始,每行输出一条路径。文件的最后一行是最少路径数。
这一题还是比较裸的,首先我们要知道:最小路径覆盖=顶点数 - 最大流
然后我们就可以开心地写代码了:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=1000005;
const int MAXM=1000005;
int d[MAXN],n,m,p[MAXN],eid,S,T,x,y,vis[MAXN];
struct A{
int v,c,next;
}e[MAXM];
void init(){
memset(p,-1,sizeof(p));
eid=0;
}
void add(int u,int v,int c){
e[eid].v=v;
e[eid].c=c;
e[eid].next=p[u];
p[u]=eid++;
}
void insert(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
int bfs(){
memset(d,-1,sizeof(d));
queue<int>q;
d[S]=0;
q.push(S);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=p[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(e[i].c>0&&d[v]==-1){
d[v]=d[u]+1;
q.push(v);
}
}
}
return (d[T]!=-1);
}
int dfs(int u,int flow){
if(u==T) return flow;
int ret=0;
for(int i=p[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(e[i].c>0&&d[v]==d[u]+1){
int tmp=dfs(v,min(flow,e[i].c));
e[i].c-=tmp;
e[i^1].c+=tmp;
flow-=tmp;
ret+=tmp;
if(!flow) break;
}
}
if(!ret) d[u]=-1;
return ret;
}
int Dinic(){
int ret=0;
while(bfs()){
ret+=dfs(S,INF);
}
return ret;
}
void dfss(int x){ // 以下部分码风受YJQ的影响
if(vis[x]) return;
vis[x]=1; printf("%d ",x);
for(int i = p[x]; i + 1; i = e[i].next){
int v = e[i].v;
if(!e[i].c && v > n) dfss(v - n); //因为容量为1,所以为0的肯定是流过的。
}
}
void print(){
for(int i = 1; i <= n; i++){
if(!vis[i]) dfss(i),printf("\n");
}
}
int main () {
init();
scanf("%d%d", &n, &m);
S=0; T=2*n+1;
for(int i = 1;i <= n; i++) insert(S, i, 1);
for(int i = 1;i <= n; i++) insert(i + n, T, 1);
for(int i = 1; i <= m; i++){
scanf("%d%d", &x, &y);
insert(x, y + n, 1);
}
int ans=n-Dinic();
print();
printf("%d",ans);
return 0;
}
(>_<)