网络流24题之最小路径覆盖问题
DAG的最小不相交路径覆盖
算法:把原图的每个点V拆成V
和V两个点,如果有一条有向边A->B,那么就加边Ax−>By
。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。
证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。
因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。
我这里给出网络流的写法,建立源点汇点,连流量为1的边,然后答案就是点数-最大流
By:大奕哥
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstring> 6 #include<queue> 7 #include<vector> 8 #include<cmath> 9 using namespace std; 10 const int N=10005; 11 int n,m,s=0,t=10000,head[N],d[N],vis[N],cnt=-1; 12 inline int read(){ 13 int x=0,f=1;char ch=getchar(); 14 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 15 while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 16 return x*f; 17 } 18 struct node{ 19 int to,nex,w; 20 }e[N<<1]; 21 void add(int x,int y,int w) 22 { 23 e[++cnt].to=y;e[cnt].w=w;e[cnt].nex=head[x];head[x]=cnt; 24 e[++cnt].to=x;e[cnt].w=0;e[cnt].nex=head[y];head[y]=cnt; 25 } 26 queue<int>q; 27 bool bfs(int x,int y) 28 { 29 memset(d,-1,sizeof(d)); 30 d[x]=0;q.push(x); 31 while(!q.empty()) 32 { 33 int x=q.front();q.pop(); 34 for(int i=head[x];i!=-1;i=e[i].nex) 35 { 36 int y=e[i].to; 37 if(!e[i].w||d[y]!=-1)continue; 38 d[y]=d[x]+1; 39 q.push(y); 40 } 41 } 42 return d[y]!=-1; 43 } 44 int dfs(int x,int w,int yy) 45 { 46 if(!w||x==yy)return w; 47 int s=0; 48 for(int i=head[x];i!=-1;i=e[i].nex) 49 { 50 int y=e[i].to; 51 if(!e[i].w||d[y]!=d[x]+1)continue; 52 int flow=dfs(y,min(w-s,e[i].w),yy); 53 if(!flow){ 54 d[y]=-1;continue; 55 } 56 e[i].w-=flow;e[i^1].w+=flow; 57 vis[x]=y;s+=flow; 58 if(s==w)return s; 59 } 60 return s; 61 } 62 int dinic() 63 { 64 int ans=0; 65 while(bfs(s,t)){ 66 ans+=dfs(s,1e9,t); 67 } 68 return ans; 69 } 70 int main() 71 { 72 n=read();m=read();int x,y; 73 memset(head,-1,sizeof(head)); 74 for(int i=1;i<=m;++i) 75 { 76 x=read();y=read(); 77 add(x,y+n,1); 78 } 79 for(int i=1;i<=n;++i) 80 { 81 add(s,i,1); 82 add(i+n,t,1); 83 } 84 int ans=dinic(); 85 for(int i=1;i<=n;++i) 86 { 87 if(vis[i]) 88 { 89 int pos=i; 90 while(pos) 91 { 92 if(pos>n)pos-=n; 93 printf("%d ",pos); 94 int x=vis[pos]; 95 vis[pos]=0; 96 pos=x; 97 } 98 printf("\n"); 99 } 100 } 101 printf("%d\n",n-ans); 102 return 0; 103 }
生命中真正重要的不是你遭遇了什么,而是你记住了哪些事,又是如何铭记的。