二分图洛谷习题

文章目录

KM算法

参考题解,可以跳题解看我只是在尝试理解
求完美匹配的最大值。其实可以理解为一个相亲问题,有n男n女,女生i对每个男生j有一个好感度 l o v e [ i ] [ j ] love[i][j] love[i][j],而男生对女生的好感度不重要,全部设置为0。

每个女生i都对自己的爱情有一个期望值 e x _ g i r l [ i ] ex\_girl[i] ex_girl[i],最理想的情况每个女生都希望自己可以和最有好感度的男生在一起,因此她们对爱情的初始期待值 e x _ g i r l [ i ] = m a x ( l o v e [ i ] [ j ] ) ex\_girl[i]=max(love[i][j]) ex_girl[i]=max(love[i][j]),在匹配的时候如果一个男生达到女生的期望值就可以和她匹配,如果达不到就免谈。

但这样可以会导致有的男生没有女生要,因此KM算法成为一个月老,它的作用是说服一些女生降低期望值,以使得所有的男生都有女生要,并且最终所有女生对所配的男生的好感度的总和最大

KM算法按顺序为每一个女生处理她们的归属问题,每个女生又可以匹配很多轮,并且同时减低对爱情的期望值,直到她自己可以匹配成功。

每一轮匹配一个男生只能被尝试匹配一次,不管那次是成功还是失败,如果某个男生可以匹配到女生就皆大欢喜,否则他知道他还差多少才能达到女生的好感度,这个差距是 g a p gap gap,因为在多轮匹配中男生会和很多个女生配对又失配,因此他会聪明地将 g a p gap gap取最小,这个最小值就是 s l a c k slack slack

每个女生匹配一轮失败,就会降低期望值,减低到什么程度才会使得下一轮有所进展呢?就是有某个在这一轮中失配的男生,在下一轮至少可以获得一个女生的芳心,因此女生的期望值应该降低到这个男生够得到,因此减去 d = m i n ( s l a c k [ i ] ) d=min(slack[i]) d=min(slack[i]) i i i表示所有失配男生。

但是降低期望值是所有这一轮匹配过的女生一起降低 d d d,且匹配过的男生也增加了 d d d的期望值,同时失配男生的 s l a c k slack slack都会减去最小的男生的 s l a c k slack slack d d d
模板题 P1559 运动员最佳匹配问题

#include<bits/stdc++.h>
using namespace std;
const int N=210,inf=0x3f3f3f3f;

int love[N][N],ex_boy[N],ex_girl[N],slack[N],mat[N],n;
bool vis_girl[N],vis_boy[N];
bool dfs(int u){
    vis_girl[u]=true;
    for(int i=1;i<=n;i++){
        if(vis_boy[i])continue;
        int gap=ex_boy[i]+ex_girl[u]-love[u][i];
        if(!gap){
            vis_boy[i]=true;
            if(!mat[i]||dfs(mat[i])){
                mat[i]=u;return true;
            }
        }
        else slack[i]=min(slack[i],gap);
    }return false;
}
int KM(){
    memset(mat,0,sizeof mat);

    for(int i=1;i<=n;i++){
        ex_girl[i]=love[i][1];
        for(int j=2;j<=n;j++){
            ex_girl[i]=max(love[i][j],ex_girl[i]);
        }
    }

    for(int i=1;i<=n;i++){
        memset(slack,0x3f,sizeof slack);
        while(1){
            memset(vis_boy,false,sizeof vis_boy);
            memset(vis_girl,false,sizeof vis_girl);
            if(dfs(i))break;
            int d=inf;
            for(int j=1;j<=n;j++)
                if(!vis_boy[j])d=min(slack[j],d);
            for(int j=1;j<=n;j++){
                if(vis_girl[j])ex_girl[j]-=d;
                if(vis_boy[j])ex_boy[j]+=d;
                else slack[j]-=d;
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
        res+=love[mat[i]][i];
    return res;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>love[i][j];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int x;cin>>x;
            love[j][i]*=x;
        }
    }
    cout<<KM()<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值