最小路径覆盖问题

来源:互联网 发布:淘宝芝士年糕哪家好吃 编辑:程序博客网 时间:2024/04/19 17:45

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




0 0
原创粉丝点击