匹配问题
小技巧:
当二分图大写没有明显界限时 就建立双向边
建双向边的时候 求出的最大匹配需要/2
运用match数组找到匹配对应点
多重匹配:
成组的匹配:一组匹配多个
只需要在普通的匹配上修改就好, 把match扩展成一个数组 并记录长度 如果组内用空就匹配,替换匹配时就把这些数组扫一遍。
Hopcroft-Krap算法
求最大匹配 时间复杂度O(sqrt(V)*E) 当匈牙利算法过不去时用,比用网络流求最大匹配还要快一点点
原理:
(1)使用BFS遍历对图的点进行分层,从X中找出一个未匹配点v,(所有v)组成第一层,接下的层是这样形成的——都是查找匹配点(增广路性质),直到在V中找到未匹配点才终止查找,对X其他未匹配点同样进行查找增广路径(BFS只分层不标记是否匹配点)
(2)使用DFS遍历查找(1)形成的增广路,找到就匹配数就累加1
(3)重复(1)(2)操作直到找不出增广路径为止
模板如下
int vis[N],matx[N],maty[N];
int dx[N],dy[N],dep;
int Find(int x){
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(!vis[y] && dx[x] == dy[y] - 1){
vis[y] = 1;
if(!maty[y] || Find(maty[y])){
maty[y] = x;
matx[x] = y;
return 1;
}
}
}
return 0;
}
bool bfs(){
queue<int> q;
dep = INF;
mst(dx,-1);
mst(dy,-1);
For(i,1,m){
if(!matx[i]){ //在x中未匹配的点
dx[i] = 0;
q.push(i); //加入队列
}
}
while(q.size()){
int x = q.front();
q.pop();
if(dep < dx[x]) continue; //增广路路径长度大于当前dep
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(dy[y] == -1){ //y 中未匹配的点
dy[y] = dx[x] + 1; //对点进行分层
if(!maty[y]) dep = dy[y]; //本次得到最大dep(层次)
else {
dx[maty[y]] = dy[y] + 1; //y是匹配点,继续延申
q.push(maty[y]);
}
}
}
}
if(dep == INF){
return 0;
}
return 1;
}
在main里
int ans = 0;
mst(matx,0);
mst(maty,0);
while(bfs()){
mst(vis,0);
For(i,1,m){/
if(!matx[i] && Find(i)) ans++;
}
}
网络流
重要概念:
增广路:是从源点到汇点的路径,其上所有边的残余容量均大于0。
分层图:在残量网络中,满足d[y] = d[x] + 1的边 x->y 构成的子图 是无环图
dinic求最大流:
时间复杂度:O(n2m) 效率高于EK算法
原理:
1.在残量网络上bfs求出层次dep,构成分层图
2.在分层图上dfs寻找增广路,再回溯更新剩余容量
3.重复1.2.
#include<bits/stdc++.h>
using namespace std;
const int N = 800,M = 20000 + 10,INF = 0x3f3f3f3f;
#define int long long
int h[N],e[M],ne[M],w[M],idx;
int d[N],now[M];
int maxflow,s,t;
void add(int x,int y,int z){
e[++idx] = y,ne[idx] = h[x], h[x] = idx,w[idx] = z;
e[++idx] = x,ne[idx] = h[y], h[y] = idx,w[idx] = 0;
}
int n,m;
bool bfs(){
memset(d,0,sizeof(d));
queue<int> q;
d[s] = 1,q.push(s);
now[s] = h[s];
while(q.size()){
int x = q.front();q.pop();
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(w[i] && !d[y]){
q.push(y);
now[y] = h[y];
d[y] = d[x] + 1;
if(y == t) return 1;
}
}
}
return 0;
}
int dinic(int x,int flow){
if(x == t) return flow;
int rest = flow,k,i;
for(i = now[x] ;i && rest ;i = ne[i]){
int y = e[i];
if(w[i] && d[y] == d[x] + 1){
k = dinic(y,min(rest,w[i]));
if(!k) d[y] = 0;
w[i] -= k;
w[i ^ 1] += k;
rest -= k;
}
}
now[x] = i;
return flow - rest;
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
idx = 1;
int flow = 0;
for(int i = 1;i<=m;i++){
int x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
}
while(bfs()){
while(flow = dinic(s,INF) ) maxflow += flow;
}
printf("%lld\n",maxflow);
system("pause");
return 0;
}
求二分图最大匹配:
对二分图 构造出虚拟源点与汇点,根据二分图构建权值为1的图,并把源点与汇点给连上 跑一遍最大流 就是二分图最大匹配
最小割
任何一个网络的最大流等于最小割割边的容量之和 ,简记为“最大流 = 最小割”。
几个好用的数学模板
求组合
LL C(LL n,LL m){
static LL Z=0,inv[N],mul[N],invMul[N];
while(Z<=n){
if(Z){
inv[Z] = Z == 1 ? 1:(mod - mod/Z) * inv[mod%Z]%mod;
mul[Z] = mul[Z-1]*Z%mod;
invMul[Z] = invMul[Z-1]*inv[Z]%mod;
}
else mul[Z] = 1,invMul[Z] = 1;
Z++;
}
return mul[n]*invMul[m]%mod*invMul[n-m]%mod;
}
质数筛
LL getPrime(LL n,bool vis[],LL prime[]){
LL tot = 0;
for(LL i = 1;i<=n;i++) vis[i] = 0;
for(LL i = 2;i<=n;i++){
if(!vis[i]) prime[tot++] = i;
for(LL j = 0;j<tot;j++){
if(prime[j] * i > n) break;
vis[prime[j] * i] = 1;
if(i%prime[j] == 0) break;
}
}
return tot;
}
快速判断质数
bool isPrime(LL n){
if(n==2||n==3||n==5) return 1;
if(n%2==0||n%3==0||n%5==0||n==1) return 0;
LL c = 7,hain[8] = {4,2,4,2,4,6,2,6};
while(c*c<=n) for(auto i : hain){
if(n%c==0) return 0;
c += i;
}
return 1;
}