题目参考 COGS 728【网络流24题】最小路径覆盖问题。。。
大意是给你一个有向图,要求你使每一个点在且仅在一条路径上,如果路径总数最少,则这个方案成为最小路径覆盖。
1、网络流解法
既然是网络流24题,我们首先考虑一下网络流的做法。参考SDOI 星际竞速,我们把每个点拆为两个点,源点向第一排点连边,汇点向第二排点两边,原图中的边由第一排点向第二排点连,跑一遍最大流,点数减去流量maxflow即为路径数,因为这样只有源点到maxflow个路径终点的边剩余流量为1(同样的,只有maxflow个路径起点到汇点剩余流量为1),这样,我们在做网络流的时候,每找到一条增广路径,我们就记录一下它是由那个点的第一排点连过来。跑完最大流后,递归输出路径即可。code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
int u,v,c;
}a[130000];
int point[500],next[130000],n,m,e,t;
int cur[500],pre[500],lev[500],gap[500],pred[500];
bool f[200]={false};
void add(int x,int y,int c)
{
e++; next[e]=point[x]; cur[x]=point[x]=e;
a[e].u=x; a[e].v=y; a[e].c=c;
e++; next[e]=point[y]; cur[y]=point[y]=e;
a[e].u=y; a[e].v=x; a[e].c=0;
}
int ISAP(int vs,int vt)
{
int u,v,i,minl,aug,maxt=0; bool f;
u=vs; gap[0]=vt-vs+1;
while (lev[vs]<vt)
{
f=false;
for (v=cur[u];v!=0;v=next[v])
if (lev[u]==lev[a[v].v]+1&&a[v].c>0)
{f=true; cur[u]=v; break;}
if (f)
{
pre[a[v].v]=v;
u=a[v].v;
if (u==vt)
{
aug=2100000000;
for (i=v;i!=0;i=pre[a[i].u])
if (aug>a[i].c)
aug=a[i].c;
maxt+=aug;
for (i=v;i!=0;i=pre[a[i].u])
{
pred[a[i].v]=a[i].u;
a[i].c-=aug;
a[i^1].c+=aug;
}
u=vs;
}
}
else
{
minl=vt;
for (i=point[u];i!=0;i=next[i])
if (minl>lev[a[i].v]&&a[i].c>0)
minl=lev[a[i].v];
gap[lev[u]]--;
if (gap[lev[u]]==0) break;
lev[u]=minl+1;
cur[u]=point[u];
gap[lev[u]]++;
if (u!=vs) u=a[pre[u]].u;
}
}
return maxt;
}
int find(int vs,int vt)
{
int i;
for (i=point[vs];i!=0;i=next[i])
if (a[i].v==vt)
return i;
}
int main()
{
int i,ii,x,y,ans,j,road[300];
scanf("%d%d",&n,&m);
e=1;
for (i=1;i<=n;++i)
{
add(0,i*2-1,1);
add(i*2,n*2+1,1);
}
for (i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
add(x*2-1,y*2,1);
}
ans=ISAP(0,2*n+1);
ans=0;
for (i=n;i>=1;--i)
if (f[i]==false)
{
ii=i;
memset(road,0,sizeof(road));
t=1; road[t]=ii;
j=find(ii*2,n*2+1);
ii*=2;
while (a[j].c==0&&f[ii/2]==false)
{
f[ii/2]=true;
ii=pred[ii];
ii++;
t++; road[t]=ii/2;
j=find(ii,n*2+1);
}
f[ii/2]=true;
for (j=t;j>=1;--j)
printf("%d ",road[j]);
printf("\n");
ans++;
}
printf("%d\n",ans);
}
2、匈牙利算法
这就是为什么此题不放在网络流学习笔记的原因了。我AC之后看了RANK 1的程序,发现似乎是二分图匹配之类的东西,后来发现这玩意儿真的可以做最小路径覆盖(貌似比网络流稍快一点,学匈牙利算法就看这篇文章好了,简单通俗易懂,大赞博主。 http://blog.csdn.net/dark_scope/article/details/8880547 code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int cd[151][151],n,m;
bool visit[151];
int match[151];
int go[151];
bool find(int u)
{
int i,now;
for (i=1;i<=cd[u][0];++i)
{
now=cd[u][i];
if (!visit[now])
{
visit[now]=true;
if (!match[now]||find(match[now]))
{
match[now]=u;
go[u]=now;
return true;
}
}
}
return false;
}
int main()
{
int st,ans=0,i,j,x,y;
scanf("%d%d",&n,&m);
for (i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
cd[x][0]++;
cd[x][cd[x][0]]=y;
}
for (i=1;i<=n;++i)
{
memset(visit,false,sizeof(visit));
if (!find(i)) ans++;
}
for (i=1;i<=n;++i)
if (!match[i])
{
st=i;
while (st!=0)
{
printf("%d ",st);
st=go[st];
}
printf("\n");
}
printf("%d\n",ans);
}