题目链接:点击这里
解题思路:
原题意可以看做原来4个人的宿舍在i宿舍现在要搬到j宿舍需要至少搬多少个人。那么新宿舍与旧宿舍里面相同的人数有几个说明这几个人就可以不用搬了。形象的说,如果从i宿舍搬到j宿舍,就看j和i宿舍现在的人里面是否存在相同的人在里面,这几个人就不用搬了,不在j里面的要从i搬到j。最后就是跑一个二分最大匹配求出不用搬的最多人数,然后用4*n减去就行了。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<stack>
#include<bitset>
using namespace std;
typedef long long int ll;
const ll inf = 0x3f3f3f3f;
const int mx = 4e2+5;
const int mod = 1e9+7;
int n,m;
bitset<405>x[105];
bitset<405>y[105];
int w[mx][mx],lx[mx],ly[mx];
int slack[mx],cp[mx];
bool visx[mx],visy[mx];
bool find(int x)
{
visx[x] = 1;
for(int i=1;i<=n;i++){
if(!visy[i]){
int val = lx[x] + ly[i] - w[x][i];
if(!val){
visy[i] = 1;
if(!cp[i]||find(cp[i])){
cp[i] = x;
return 1;
}
}else slack[i] = min(slack[i],val);
//找到没有在增广路径的最小松弛值,使得lx[x]+ly[i]-w[x][i]=0
}
}
return 0;
}
int KM()
{
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) slack[j] = inf;
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
while(!find(i)){
int d = inf;
for(int j=1;j<=n;j++)
if(!visy[j]) d = min(d,slack[j]);
for(int j=1;j<=n;j++)//对于增广路径上的左侧减去最小松弛值
if(visx[j]) lx[j] -= d;
for(int j=1;j<=n;j++)
if(visy[j]) ly[j] += d;//对于增广路径上的右侧点加上d
else slack[j] -= d;//相应松弛值也应该减少d
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
}
}
int ans = 0;
for(int i=1;i<=n;i++)
ans += w[cp[i]][i];
return ans;
}
int solve(){
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){
bitset<405>tmp = x[i]&y[j];
w[i][j] = tmp.count();
//cout<<w[i][j]<<endl;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)//初始化二分图左侧点最大值为最大边权重
lx[i] = max(lx[i],w[i][j]);
return 4*n-KM();
}
int main(){
memset(lx,-inf,sizeof(lx));
int t,q,ca = 1;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= 4; j++){
int id ;
scanf("%d",&id);
x[i].set(id);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= 4; j++){
int id;
scanf("%d",&id);
y[i].set(id);
}
printf("%d\n",solve());
return 0;
}