题目参考 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); }