2020-11-22周总结

匹配问题

小技巧:
当二分图大写没有明显界限时 就建立双向边
建双向边的时候 求出的最大匹配需要/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;
}

刷题截屏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值