二分图知识总结

二分图:简单的说就是把图中的点分成二组,并且使得所有的边都跨越组的边界。

匹配:任意二条边都没有公共都顶点

最大匹配:一个图的匹配边数最多

完美匹配:一个图中的某个匹配中,所有的顶点都是匹配点

交替路:从一个未匹配的点出发,走交替路 依此经过匹配边 非匹配边 匹配边 ......形成的路径叫交替路

增广路:从一个未匹配点出发走交替路,经过一个未匹配的点

最大独立数:选取最多的点 使任意所选二点均不相连

最小点覆盖数:选取最少的点 是任意一条边至少有一个端点被选择

最小路径覆盖数:对于一个DAG(有向无环图)选取最少点边 使得每个顶点属于且仅属于一条路径

定理1:最大匹配数=最小点覆盖数

定理2:最大匹配数=最大独立数

定理3:最小路径覆盖数=顶点数-最大匹配数

下面是二分图的判断:

const int MAX=10010;
vector<int>G[MAX];
int edge[MAX][MAX];
int col[MAX];
int n;
int dfs(int u,int c){
    col[u]=c;
    for(int i=0;i<n;i++){
        if(edge[u][i]==1){
            if(col[i]==c) return 0;
            if(col[i]==0&&!dfs(i,-c)) return 0;
        }
    }
    return 1;
}
int bfs(int u){
    queue<int>q;
    q.push(u);
    col[u]=1;
    while (!q.empty()) {
        int v=q.front();
        q.pop();
        for(int i=0;i<G[v].size();i++){
            int x=G[v][i];
            if(col[x]==0){
                col[x]=-col[v];
                q.push(x);
            }
            else {
                if(col[x]==col[v])
                    return 0;
            }
        }
    }
    return 1;
}
void solve(){
    for(int i=1;i<=n;i++){
        if(col[i]==0&&!bfs(i))
        {
            cout<<"NO"<<endl;
            return;
        }
    }
    cout<<"YES"<<endl;
}

最大匹配的匈牙利算法:

const int  MAX=100010;
vector<int>G[MAX];
int n,m;
int vis[MAX],pei[MAX];
int dfs(int u){
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!vis[v]){
            vis[v]=1;
            if(!pei[v]||dfs(pei[v])){
                pei[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
void solve(){
    int ans=0;
    for(int i=1;i<=n;i++){
        mms(vis,0);
        if(dfs(i))
            ans++;
    }
    cout<<ans<<endl;
}

最大权的匹配KM:

求二分图中最大匹配的边权和最大

 

const int MAX=1e3+10;
int wx[MAX],wy[MAX];
int matchx[MAX];
int matchy[MAX];
int visx[MAX];
int visy[MAX];
int cntx=MAX;
int cnty=MAX;
int mp[MAX][MAX];
int d;
int dfs(int u){
    visx[u]=1;
    for(int v=0;v<cnty;v++){
        if(!visy[v]&&mp[u][v]!=INF){
            int t=wx[u]+wy[v]-mp[u][v];
            if(t==0){
                visy[v]=1;
                if(matchy[v]==-1||dfs(matchy[v])){
                    matchx[u]=v;
                    matchy[v]=u;
                    return 1;
                }
            }
            else if(t>0){
                d=min(d,t);
            }
        }
    }
    return 0;
}
int km(){
    mms(matchx,-1);
    mms(matchy,-1);
    mms(wx,0);
    mms(wy,0);
    for(int i=0;i<cntx;i++){
        for(int j=0;j<cnty;j++){
            if(mp[i][j]==INF)
                continue;
            wx[i]=max(wx[i],mp[i][j]);
        }
    }
    for(int i=0;i<cntx;i++){
        while (1) {
            d=INF;
            mms(visx,0);
            mms(visy,0);
            if(dfs(i)) break;
            for(int j=0;j<cntx;j++)
                if(visx[j]) wx[j]-=d;
            for(int j=0;j<cnty;j++)
                if(visy[j]) wy[j]+=d;
            
        }
    }
    int ans=0;
    for(int i=0;i<cntx;i++){
        if(matchx[i]!=-1)
            ans+=mp[i][matchx[i]];
    }
    return ans;
}
int n,k;
int main(){
    
        cin>>n;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++)
                cin>>mp[i][j];
        }
        cout<<km()<<endl;
    
    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭晋龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值