最小路径覆盖问题

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值