Evanyou Blog 彩带

网络流二十四题之最小路径覆盖问题

  先上题目传送门

  最小路径覆盖其实是一类题目,一般用二分图匹配或者网络流都可以做。相关定理:最小路径覆盖数=顶点数-最大割(即最大匹配)

  这道题的要求除了求出最小路径覆盖数之外还要求输出每一条路径,这里蒟蒻用的是最简单粗暴的深搜,从每一个路径覆盖的起点开始一个个搜索直到输出所有点。

  奉上用Dinic算法做的正解:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<iostream>
#include<algorithm>
#define inf 1e9
using namespace std;
const int N=10010;
int n,m,dis[N<<1],ans;
int head[N<<1],size=1;
int fa[N<<1],start,endd;
struct Node{
  int from,to,val,next;
}edge[N<<1];
queue<int>team;
inline int read()
{
  char ch=getchar();int num=0;bool flag=false;
  while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
  while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
  return flag?-num:num;
}
inline void add(int x,int y,int z)
{
  edge[++size].from=x;
  edge[size].to=y;
  edge[size].val=z;
  edge[size].next=head[x];
  head[x]=size;
}
inline void add_edge(int x,int y)
{add(x,y,1);add(y,x,0);}
inline bool bfs()
{
  memset(dis,-1,sizeof(dis));
  while(!team.empty())team.pop();
  team.push(start);dis[start]=0;
  while(!team.empty()){
    int x=team.front();team.pop();
    for(int i=head[x];i!=-1;i=edge[i].next){
      int y=edge[i].to;
      if(edge[i].val>0&&dis[y]==-1){
    dis[y]=dis[x]+1;team.push(y);
      }
    }
  }
  return dis[endd]>-1;
}
inline int dinic(int u,int flow)
{
  if(u==endd||flow==0)return flow;
  int now=0;
  for(int i=head[u];i!=-1;i=edge[i].next){
    int v=edge[i].to;
    if(edge[i].val>0&&dis[v]==dis[u]+1){
      int ka=dinic(v,min(flow-now,edge[i].val));
      edge[i].val-=ka;
      edge[i^1].val+=ka;
      now+=ka;
      if(now==flow)break;
    }
  }
  return now;
}
inline int find(int x)
{return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void print(int u)
{
  printf("%d ",u);
  for(int i=head[u];i!=-1;i=edge[i].next){
    int v=edge[i].to;
    if(edge[i].val==0&&v>n)
      print(v-n);
  }
}
void ready()
{
  memset(head,-1,sizeof(head));
  n=read();m=read();start=0;endd=n<<1|1;
  for(int i=1;i<=m;i++){
    int x=read();int y=read();
    add_edge(x,y+n);}
  for(int i=1;i<=n;i++){
    add_edge(start,i);
    add_edge(i+n,endd);
  }
}
void work()
{
  while(bfs())
    ans+=dinic(start,inf);
  for(int i=1;i<=n;i++)fa[i]=i;
  for(int i=2;i<=size;i++){
    int u=edge[i].from,v=edge[i].to;
    if(u>start&&u<=n&&v>n&&v<endd&&edge[i].val==0)
      fa[find(v-n)]=find(u);}
    for(int i=1;i<=n;i++)
      if(find(i)==i)
    print(i),printf("\n");
    printf("%d",n-ans);
}
int main()
{
  ready();
  work();
  return 0;
}

  这里再上一份用二分图匹配算法做的最小路径覆盖模板(不输出路径):

//It is made by HolseLee on 30th Dec 2017
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=10010;
int n,m,ans,ma[N];
bool vis[N];
vector<int>team[N];
inline int read()
{
  char ch=getchar();int num=0;bool flag=false;
  while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
  while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
  return flag?-num:num;
}
inline bool dfs(int u)
{
  for(int i=0;i<team[u].size();i++){
    int v=team[u][i];
    if(vis[v])continue;
    vis[v]=true;
    if(ma[v]==-1||dfs(ma[v])){
      ma[v]=u;
      return true;}
  }
  return false;
}
void ready()
{
  memset(ma,-1,sizeof(ma));
  n=read();m=read();
  for(int i=1;i<=n;i++)
    team[i].clear();
  for(int i=1;i<=m;i++){
    int x=read();int y=read();
    team[x].push_back(y);
  }
}
void work()
{
  int ret=0;
  for(int i=1;i<=n;i++){
    memset(vis,false,sizeof(vis));
    ret+=dfs(i);
  }
  ans=n-ret;
  printf("%d",ans);
}
int main()
{
  ready();
  work();
  return 0;
}

 

posted @ 2017-12-30 10:48  HolseLee  阅读(268)  评论(0编辑  收藏  举报