二分图??(自用,勿看)
注意:这是一篇个人学习笔记,如果有人因为某些原因点了进来并且要看一下,请一定谨慎地阅读,因为可能存在各种奇怪的错误,如果有人发现错误请指出谢谢!
只能先背着了...
资料:https://www.cnblogs.com/jianglangcaijin/p/6035945.html
二分图的最小顶点覆盖
定义:假如选了一个点就相当于覆盖了以它为端点的所有边。最小顶点覆盖就是选择最少的点来覆盖所有的边。
方法:最小顶点覆盖=最大匹配。
二分图的最大独立集
定义:选出一些顶点使得这些顶点两两不相邻,则这些点构成的集合称为独立集。找出一个包含顶点数最多的独立集称为最大独立集。
方法:最大独立集=所有顶点数-最小顶点覆盖
二分图的最大团
定义:对于一般图来说,团是一个顶点集合,且由该顶点集合诱导的子图是一个完全图,简单说,就是选出一些顶点,这些顶点两两之间都有边。最大团就是使得选出的这个顶点集合最大。对于二分图来说,我们默认为左边的所有点之间都有边,右边的所有顶点之间都有边。那么,实际上,我们是要在左边找到一个顶点子集X,在右边找到一个顶点子集Y,使得X中每个顶点和Y中每个顶点之间都有边。
方法:最大团=补图的最大独立集。补图的定义是:对于二分图中左边一点x和右边一点y,若x和y之间有边,那么在补图中没有,否则有。
资料:https://blog.csdn.net/flynn_curry/article/details/52966283
资料:https://blog.csdn.net/wall_f/article/details/8187144
二分图的最小边覆盖
定义:假如选了一条边就相当于覆盖了它的两个端点。最小边覆盖就是选择最少的边来覆盖所有的顶点。
方法:最小边覆盖=最大独立集=所有顶点数-最大匹配。
二分图最大匹配
匈牙利算法资料:https://www.cnblogs.com/wangjunyan/p/5563154.html
当然也可以网络流直接跑,对于给定二分图,S向一边的点连边,另一边的点向T连边,跑S到T最大流即可;复杂度的话,用dinic好像可以证sqrt(n)*m
资料(一些证明?):https://blog.csdn.net/u013043514/article/details/48206577
二分图的最小路径覆盖
定义:用最少的不相交路径覆盖所有顶点。
定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A−>B,那么就加边(Ax,By)。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。
不需要输出方案:http://210.33.19.103/contest/877/problem/3
需要输出方案:https://www.luogu.org/problemnew/show/P2764
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 #define fi first 8 #define se second 9 #define mp make_pair 10 #define pb push_back 11 typedef long long ll; 12 typedef unsigned long long ull; 13 typedef pair<int,int> pii; 14 namespace F 15 { 16 17 struct E 18 { 19 int to,nxt,from,cap,flow; 20 }e[2001000]; 21 int f1[10100],ne=1; 22 int S,T,n; 23 int d[10100]; 24 bool bfs() 25 { 26 int k,u; 27 memset(d,0,sizeof(int)*(n+1)); 28 queue<int> q; 29 q.push(S);d[S]=1; 30 while(!q.empty()) 31 { 32 u=q.front();q.pop(); 33 for(k=f1[u];k;k=e[k].nxt) 34 if(!d[e[k].to]&&e[k].cap>e[k].flow) 35 { 36 d[e[k].to]=d[u]+1; 37 if(e[k].to==T) return 1; 38 q.push(e[k].to); 39 } 40 } 41 return 0; 42 } 43 int cur[10100]; 44 int dfs(int u,int x) 45 { 46 if(u==T||x==0) return x; 47 int flow=0,f; 48 for(int &k=cur[u];k;k=e[k].nxt) 49 if(e[k].cap>e[k].flow&&d[e[k].to]==d[u]+1) 50 { 51 f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow)); 52 e[k].flow+=f;e[k^1].flow-=f;flow+=f; 53 if(flow==x) return flow; 54 } 55 return flow; 56 } 57 int solve() 58 { 59 int flow=0; 60 while(bfs()) 61 { 62 memcpy(cur,f1,sizeof(int)*(n+1)); 63 flow+=dfs(S,0x3f3f3f3f); 64 } 65 return flow; 66 } 67 68 void me(int a,int b,int c) 69 { 70 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne; 71 e[ne].from=a;e[ne].cap=c; 72 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne; 73 e[ne].from=b;e[ne].cap=0; 74 } 75 76 } 77 inline int read() 78 { 79 int x = 0, f = 1; 80 char ch = getchar(); 81 while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); } 82 while ('0' <= ch && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); 83 return x * f; 84 } 85 int pre[100100]; 86 int n,m; 87 bool vis[100100]; 88 void work() 89 { 90 using namespace F; 91 int i; 92 for(i=2;i<=ne;i+=2) 93 { 94 if(e[i].flow==1&&e[i].from>=1&&e[i].from<=::n&&e[i].to>=::n+1&&e[i].to<=2*::n) 95 { 96 //printf("t%d %d\n",e[i].from,e[i].to); 97 pre[e[i].to-::n]=e[i].from; 98 vis[e[i].from]=1; 99 } 100 } 101 } 102 void out(int p) 103 { 104 vis[p]=1; 105 if(pre[p]) out(pre[p]); 106 printf("%d ",p); 107 } 108 int main() 109 { 110 int i,a,b; 111 scanf("%d%d",&n,&m); 112 { 113 using F::me; 114 for(i=1;i<=m;i++) 115 { 116 scanf("%d%d",&a,&b); 117 me(a,b+n,1); 118 } 119 F::S=n+n+1;F::T=n+n+2;F::n=n+n+2; 120 for(i=1;i<=n;i++) me(F::S,i,1); 121 for(i=1;i<=n;i++) me(i+n,F::T,1); 122 } 123 int an=n-F::solve(); 124 work(); 125 for(i=1;i<=n;i++) 126 { 127 if(!vis[i]) 128 { 129 out(i); 130 puts(""); 131 } 132 } 133 printf("%d",an); 134 return 0; 135 }
二分图最大权完美匹配
板子:https://www.luogu.org/problemnew/show/P4014
km算法先放着了..
可以按二分图最大匹配的方法建网络流的图,然后新图中,每条原二分图中的边对应的边给上对应费用的相反数,其他边给费用0。跑最小费用最大流,如果最大流没有等于点数就是没有完美匹配,否则有。最后的答案就是最小费用的相反数;输出方案跟二分图最大匹配是类似的
二分图最大权任意(不一定要完美)匹配
km的板子(网络流A不了):http://uoj.ac/problem/80
km算法先放着了..
网络流的板子:http://210.33.19.103/problem/2155
网络流的做法,就是用完美匹配的做法,但是每次增广完都统计一下当前的费用,取所有费用的最小值的相反数作为最终答案(当然更新费用时可能也要记录一下方案)
显然每次增广只可能恰好增加1的流量,所以是对的
这个的复杂度稍微比费用流低一点,因为总流量最多只有原图中点数级别,每次增广最少增加1的流量,增广次数就是O(n)的
用spfa费用流复杂度是n*n*m
1 %:pragma GCC optimize(3) 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 #define fi first 9 #define se second 10 #define mp make_pair 11 #define pb push_back 12 typedef long long ll; 13 typedef unsigned long long ull; 14 typedef pair<int,int> pii; 15 void work(); 16 namespace F 17 { 18 19 struct E 20 { 21 int to,nxt,d,from,cap,flow; 22 }e[401000]; 23 int f1[5010],ne=1; 24 int S,T,n; 25 bool inq[5010]; 26 ll d[5010];int pre[5010],minn[5010]; 27 int flow;ll cost,co2; 28 void solve() 29 { 30 int k,u; 31 flow=cost=co2=0; 32 while(1) 33 { 34 memset(d,0x3f,sizeof(ll)*(n+1)); 35 memset(inq,0,sizeof(bool)*(n+1)); 36 queue<int> q; 37 q.push(S);d[S]=0;pre[S]=0;inq[S]=1;minn[S]=0x3f3f3f3f; 38 while(!q.empty()) 39 { 40 u=q.front();q.pop(); 41 inq[u]=0; 42 //if(u==T) break; 43 for(k=f1[u];k;k=e[k].nxt) 44 if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].to]) 45 { 46 d[e[k].to]=d[u]+e[k].d; 47 pre[e[k].to]=k; 48 minn[e[k].to]=min(minn[u],e[k].cap-e[k].flow); 49 if(!inq[e[k].to]) 50 { 51 inq[e[k].to]=1; 52 q.push(e[k].to); 53 } 54 } 55 } 56 if(d[T]==0x3f3f3f3f3f3f3f3f) break; 57 flow+=minn[T];cost+=d[T]*minn[T]; 58 for(k=pre[T];k;k=pre[e[k].from]) 59 { 60 e[k].flow+=minn[T];e[k^1].flow-=minn[T]; 61 } 62 if(cost<co2) 63 { 64 co2=cost; 65 work(); 66 } 67 } 68 } 69 void me(int a,int b,int c,int d) 70 { 71 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a; 72 e[ne].cap=c;e[ne].d=d; 73 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b; 74 e[ne].cap=0;e[ne].d=-d; 75 } 76 77 } 78 int an[5010]; 79 int n1,n2,m; 80 void work() 81 { 82 using namespace F; 83 int i,k; 84 for(i=1;i<=n1;i++) 85 { 86 for(k=f1[i];k;k=e[k].nxt) 87 if(n1+1<=e[k].to&&e[k].to<=n1+n2&&e[k].flow==1) 88 { 89 an[i]=e[k].to-n1; 90 //printf("%d ",e[k].to-n1); 91 goto xxx; 92 } 93 an[i]=0;//printf("%d ",0); 94 xxx:; 95 } 96 } 97 int main() 98 { 99 int i,a,b,c; 100 scanf("%d%d%d",&n1,&n2,&m); 101 for(i=1;i<=m;i++) 102 { 103 scanf("%d%d%d",&a,&b,&c); 104 F::me(a,b+n1,1,-c); 105 } 106 F::n=n1+n2+2;F::S=n1+n2+1;F::T=n1+n2+2; 107 for(i=1;i<=n1;i++) F::me(F::S,i,1,0); 108 for(i=1;i<=n2;i++) F::me(i+n1,F::T,1,0); 109 F::solve(); 110 printf("%lld\n",-F::co2); 111 for(i=1;i<=n1;i++) printf("%d ",an[i]); 112 return 0; 113 }
用原始对偶复杂的是n*m*log
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 #include<ext/pb_ds/assoc_container.hpp> 7 #include<ext/pb_ds/priority_queue.hpp> 8 using namespace std; 9 #define fi first 10 #define se second 11 #define mp make_pair 12 #define pb push_back 13 typedef long long ll; 14 typedef unsigned long long ull; 15 typedef pair<ll,int> pli; 16 void work(); 17 namespace F 18 { 19 20 struct E 21 { 22 int to,nxt,d,from,cap,flow; 23 }e[2010000]; 24 int f1[1010],ne=1; 25 int S,T,n; 26 bool inq[1010],*vis=inq; 27 ll d[1010]; 28 ll h[1010]; 29 int flow;ll cost,co2; 30 typedef __gnu_pbds::priority_queue<pli,greater<pli> > pq; 31 pq::point_iterator it[1010]; 32 bool spfa() 33 { 34 int u,k,k1; 35 memset(d,0x3f,sizeof(d[0])*(n+1)); 36 memset(inq,0,sizeof(inq[0])*(n+1)); 37 queue<int> q; 38 q.push(T);d[T]=0;inq[T]=1; 39 while(!q.empty()) 40 { 41 u=q.front();q.pop(); 42 inq[u]=0; 43 for(k1=f1[u];k1;k1=e[k1].nxt) 44 { 45 k=k1^1; 46 if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from]) 47 { 48 d[e[k].from]=d[u]+e[k].d; 49 if(!inq[e[k].from]) 50 { 51 inq[e[k].from]=1; 52 q.push(e[k].from); 53 } 54 } 55 } 56 } 57 return d[S]!=0x3f3f3f3f3f3f3f3f; 58 } 59 bool dij() 60 { 61 int i,u,k,k1;pli t; 62 memset(d,0x3f,sizeof(d[0])*(n+1)); 63 memset(vis,0,sizeof(vis[0])*(n+1)); 64 pq q; 65 for(i=1;i<=n;i++) it[i]=q.end(); 66 it[T]=q.push(mp(0,T));d[T]=0; 67 while(!q.empty()) 68 { 69 t=q.top();q.pop(); 70 u=t.se; 71 if(vis[u]) continue; 72 vis[u]=1; 73 for(k1=f1[u];k1;k1=e[k1].nxt) 74 { 75 k=k1^1; 76 if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from]) 77 { 78 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from]; 79 if(it[e[k].from]!=q.end()) q.modify(it[e[k].from],mp(d[e[k].from],e[k].from)); 80 else it[e[k].from]=q.push(mp(d[e[k].from],e[k].from)); 81 } 82 } 83 } 84 return d[S]!=0x3f3f3f3f3f3f3f3f; 85 } 86 void update() 87 { 88 for(int i=1;i<=n;i++) h[i]+=d[i]; 89 } 90 int dfs(int u,int x) 91 { 92 if(u==T||x==0) return x; 93 int flow=0,f; 94 vis[u]=1; 95 for(int k=f1[u];k;k=e[k].nxt) 96 if(!vis[e[k].to]&&e[k].cap>e[k].flow&&h[e[k].to]==h[u]-e[k].d) 97 { 98 f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow)); 99 e[k].flow+=f;e[k^1].flow-=f;flow+=f; 100 if(flow==x) return flow; 101 } 102 return flow; 103 } 104 void augment() 105 { 106 int f; 107 while(1) 108 { 109 memset(vis,0,sizeof(vis[0])*(n+1)); 110 f=dfs(S,0x3f3f3f3f); 111 if(!f) break; 112 flow+=f;cost+=f*h[S]; 113 if(cost<co2) 114 { 115 co2=cost; 116 work(); 117 } 118 } 119 } 120 void solve() 121 { 122 flow=cost=0; 123 memset(h,0,sizeof(h[0])*(n+1)); 124 if(!spfa()) return; 125 do 126 { 127 update(); 128 augment(); 129 }while(dij()); 130 } 131 void me(int a,int b,int c,int d) 132 { 133 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a; 134 e[ne].cap=c;e[ne].d=d; 135 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b; 136 e[ne].cap=0;e[ne].d=-d; 137 } 138 139 } 140 141 int an[1010]; 142 int n1,n2,m; 143 void work() 144 { 145 using namespace F; 146 int i,k; 147 for(i=1;i<=n1;i++) 148 { 149 for(k=f1[i];k;k=e[k].nxt) 150 if(n1+1<=e[k].to&&e[k].to<=n1+n2&&e[k].flow==1) 151 { 152 an[i]=e[k].to-n1; 153 goto xxx; 154 } 155 an[i]=0; 156 xxx:; 157 } 158 } 159 int main() 160 { 161 int i,a,b,c; 162 scanf("%d%d%d",&n1,&n2,&m); 163 for(i=1;i<=m;i++) 164 { 165 scanf("%d%d%d",&a,&b,&c); 166 F::me(a,b+n1,1,-c); 167 } 168 F::n=n1+n2+2;F::S=n1+n2+1;F::T=n1+n2+2; 169 for(i=1;i<=n1;i++) F::me(F::S,i,1,0); 170 for(i=1;i<=n2;i++) F::me(i+n1,F::T,1,0); 171 F::solve(); 172 printf("%lld\n",-F::co2); 173 //for(i=1;i<=n1;i++) printf("%d ",an[i]); 174 return 0; 175 }