题目描述:
一间学校有 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;
}