2018牛客暑假多校第五场 E(二分图最大权匹配)

题目描述:

一间学校有 n 间宿舍,每间宿舍有 4 个人,给出这个学校第一学年的宿舍安排
现在第二学年需要换寝室,换寝室是学生自己组队,每四个人抱团,现在你需要给他们安排具体的宿舍位置,使得换宿舍的人数量尽可能少1<=n<=100

题目分析:

    首先看数据范围,我们可以发现数据范围比较小,因此我们可以往图论/网络流的方向去想。我们画出关系图之后可以发现,现在的宿舍人数和更换宿舍之后的人数存在一定的匹配关系,因此我们就需要考虑二分图的算法。而因为题目要求我们求出的是最小的交换次数,而最小的交换次数就等价于两个宿舍中最小的不匹配的人数的个数,而这个最小的不匹配的个数我们可以通过总人数n-最大的匹配的人数求出,因为我们就可以将题目转化为一道二分图最大权匹配的问题。

    之后就需要考虑建图的问题,因为我们现在求的是更替前后宿舍的最大匹配个数,因此我们只需要把新旧两个宿舍分别作为二分图的左右半边即可。之后我们只需对每一个宿舍预处理出来既在旧宿舍又在新宿舍的人数,并将之作为边权即可。

    最后套上KM模板跑一个二分图最大权匹配即可。

代码:


#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=110;
 
int nx,ny;
int g[N][N];
int linker[N],lx[N],ly[N];
int slack[N];
bool visx[N],visy[N];
//二分图最大权匹配模板 
bool dfs(int x){
    visx[x]=true;
    for(int y=0;y<ny;++y){
        if(visy[y]) continue;
        int tmp=lx[x]+ly[y]-g[x][y];
        if(tmp==0){
            visy[y]=true;
            if(linker[y]==-1||dfs(linker[y])){
                linker[y]=x;
                return true;
            }
        }
        else if(slack[y]>tmp) slack[y]=tmp;
    }
    return false;
}
 
int KM(){
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;++i){
        lx[i]=-INF;
        for(int j=0;j<ny;++j){
            if(g[i][j]>lx[i]) lx[i]=g[i][j];
        }
    }
    for(int x=0;x<nx;++x){
        for(int i=0;i<ny;++i) slack[i]=INF;
        while(1){
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(dfs(x)) break;
            int d=INF;
            for(int i=0;i<ny;++i){
                if(!visy[i] && d>slack[i]) d=slack[i];
            }
            for(int i=0;i<nx;++i){
                if(visx[i]) lx[i]-=d;
            }
            for(int i=0;i<ny;++i){
                if(visy[i]) ly[i]+=d;
                else slack[i]-=d;
            }
        }
    }
    int res=0;
    for(int i=0;i<ny;++i){
        if(linker[i]!=-1) res+=g[linker[i]][i];
    }
    return res;
}
int tmp[105][4];
int tmp1[105][4];
int n;
void getgra(){//获得边权,即在旧宿舍又在新宿舍中人的人数
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=0;k<4;k++){
                for(int l=0;l<4;l++){
                    if(tmp[i][k]==tmp1[j][l]) g[i][j]++;
                }
            }
        }
    }
}
int main(){
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>tmp[i][0]>>tmp[i][1]>>tmp[i][2]>>tmp[i][3];
    }
    for(int i=0;i<n;++i){
        cin>>tmp1[i][0]>>tmp1[i][1]>>tmp1[i][2]>>tmp1[i][3];
    }
    getgra();
    nx=ny=n;
    int ans=n*4-KM();
    cout<<ans<<endl;
    return 0;
}

 

posted @   ChenJr  阅读(202)  评论(0编辑  收藏  举报
编辑推荐:
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
阅读排行:
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· 一个基于 .NET 开源免费的异地组网和内网穿透工具
· 《HelloGitHub》第 108 期
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单
点击右上角即可分享
微信分享提示