算法实现题8-3 最小路径覆盖问题(习题 8-13)
´问题描述:
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个
顶点恰好在 P 的一条路上,则称 P是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶
点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是 G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图G 的最小路径覆盖。
提示:设V={1,2,º ,n},构造网络G1=(V1,E1)如下:
{ } { } n n y y y x x x , , , , , , V1 1 0 1 0 » = ,
{ } { } { } E j i y x V i y y V i x x j i i i
Œ » Œ » Œ = ) . ( : ) , ( : ) , ( : ) , ( E1 0 0
每条边的容量均为1。求网络G1的( 0 x ,
0 y )最大流。
´编程任务:
对于给定的给定有向无环图G,编程找出 G的一个最小路径覆盖。
´数据输入:
由文件input.txt提供输入数据。文件第1 行有 2个正整数 n和 m。n是给定有向无环图
G 的顶点数, m是G 的边数。 接下来的m行, 每行有 2 个正整数 i和 j, 表示一条有向边(i,j)。
´结果输出:
程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出
有两种解法:
1、网络流。不懂
2、二分图匹配。
很好理解。。把一个点拆成两个点,并分成二分图。原来(i,j)∈E0,则(i,j')属于E。
求一个最大匹配。如果左半集中一个点i与右半集j'中一个点匹配,认为j‘是i的后继(因为在这个问题中,一个点一定只有一个后继!!)
最后我们容易得出,如果i有后继,i'无前驱,则表明i是一条路径的起点。如果i有后继且i'有前驱,则i是路径中的点。如果i无后继,而i'有前驱则i是终点。
我们找到所有终点即路径数。等于总结点数减去最大匹配数。
#include <cstring>
#include <cstdio>
struct node
{
long ind;
node* nxt;
};
node* head[100000];
bool vis[100000];
long lnk[100000];
long ans = 0;
long to[100000];
void insert(long a,long b)
{
node* nn = new node;
nn -> ind = b;
nn -> nxt = head[a];
head[a] = nn;
}
bool Hungary(long u)
{
for (node* vv=head[u];vv;vv=vv->nxt)
{
long v = vv -> ind;
if (!vis[v])
{
vis[v] = true;
if (!lnk[v]||Hungary(lnk[v]))
{
lnk[v] = u;
return true;
}
}
}
return false;
}
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
long n;long m;
scanf("%ld%ld",&n,&m);
for (long i=1;i<m+1;i++)
{
long i;long j;
scanf("%ld%ld",&i,&j);
insert(i,j);
}
for (long i=1;i<n+1;i++)
{
memset(vis,0,sizeof vis);
if (Hungary(i)) ans++;
}
for (long i=1;i<n+1;i++)
{
to[lnk[i]] = i;
}
for (long i=1;i<n+1;i++)
{
if (to[i] && !lnk[i])
{
long ths = i;
while (ths)
{
printf("%ld ",ths);
ths = to[ths];
}
printf("\n");
}
}
printf("%ld",n-ans);
return 0;
}