题目大意:
对于给定的有向无环图,找出其中的最小路径覆盖,并打印出第一条路径。
题目测试数据与数据范围:
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 4 7 10 11
2 5 8
3 6 9
3
顶点数与边数的范围不定。
题目分折:
有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决。构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi。对于原图中存在的每条边(i,j),在二分图中连接边(Xi,Yj)。然后把二分图最大匹配模型转化为网络流模型,求网络最大流。最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数。沿着匹配边查找,就是一个路径上的点,输出所有路径即可。对于一个路径覆盖,有如下性质:
1、每个顶点属于且只属于一个路径。
2、路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。
小乐一下:
所以我们可以把每个顶点理解成两个顶点,一个是出发,一个是目标,建立二分图模型。该二分图的任何一个匹配方案,都对应了一个路径覆盖方案。如果匹配数为0,那么显然路径数=顶点数。每增加一条匹配边,那么路径覆盖数就减少一个,所以路径数=顶点数 - 匹配数。要想使路径数最少,则应最大化匹配数,所以要求二分图的最大匹配。注意,此建模方法求最小路径覆盖仅适用于有向无环图,
我的代码,无论你怎么说,我总是觉得刘汝佳的代码格式很优秀,当然,更好更简洁的由你来实现,不能直接复制。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 0x3fffffff;
const int maxn = 1005;
struct Edge{
int from,to,cap,flow;
};
struct Dinic{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void Init(){
for(int i = 0;i<maxn;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis,0,sizeof(vis));
queue<int> Q;
Q.push(s);
d[s] = 0;
vis[s] = 1;
while(!Q.empty()){
int x = Q.front();Q.pop();
for(int i = 0;i<G[x].size();i++){
Edge & e = edges[G[x][i]];
if(!vis[e.to] && e.cap > e.flow){
vis[e.to] = 1;
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t || a==0) return a;
int flow = 0,f;
for(int &i = cur[x];i<G[x].size();i++){
Edge &e = edges[G[x][i]];
if(d[x] + 1 == d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a==0) break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s = s;
this->t = t;
int flow = 0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow += DFS(s,INF);
}
return flow;
}
void Topath(int n){
memset(vis,false,sizeof(vis));
for(int u = 1;u<=n;u++){
int tmp = u;
if(vis[tmp]) continue;
int first = 1;
while(!vis[tmp]){
vis[tmp] = true;
if(first) {printf("%d",tmp);first = 0;}
else printf(" %d",tmp);
for(int i = 0;i<G[tmp].size();i++){
if(edges[G[tmp][i]].flow > 0){
tmp = edges[G[tmp][i]].to-n;
break;
}
}
}
printf("\n");
}
}
};
int main(){
Dinic Graph;
int i,u,v,m,n;
int a,b;
while(scanf("%d%d",&n,&m)!=EOF){
Graph.Init();
for(i = 1;i<=n;i++) Graph.AddEdge(0,i,1);
for(i = n+1;i<=2*n;i++) Graph.AddEdge(i,2*n+1,1);
for(i = 1;i<=m;i++){
scanf("%d%d",&a,&b);
Graph.AddEdge(a,b+n,1);
}
printf("%d\n",n-Graph.Maxflow(0,2*n+1));
Graph.Topath(n);
}
return 0;
}
伟大的梦想成就伟大的人,从细节做好,从点点滴滴做好,从认真做好。