将一个房间当做一个点,后来的房间与之前的房间中不用搬的人作为权值,建图,跑最大权匹配,就是不用搬的人数,在减一下即可,KM算法,O(n^3)
#include<cstring>
#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
#define N 200
#define INF 0x3f3f3f3f
int nx,ny;
int slack[N];
int match[N];
int mpt[N][N];
int lx[N],ly[N];
int sx[N],sy[N];
map <int,int> mp;
struct P
{
int a,b,c,d;
}p[2*N];
int FindPath(int u)
{
sx[u]=1;
for(int v=1; v<=ny; v++)
{
int tmp=lx[u]+ly[v]-mpt[u][v];
if(!sy[v] && tmp==0)
{
sy[v]=1;
if(match[v]==-1 || FindPath(match[v]))
{
match[v]=u;
return 1;
}
}
else if(tmp<slack[v])
{
slack[v]=tmp;
}
}
return 0;
}
int KM()
{
int i,j,u;
memset(match,-1,sizeof(match));
for(i=1; i<=nx; i++)
{
lx[i]=-1;
ly[i]=0;
for(j=1; j<=ny; j++)
{
if(mpt[i][j]>lx[i]) lx[i]=mpt[i][j];
}
}
for(u=1; u<=nx; u++)
{
for(i=1; i<=ny; i++)
{
slack[i]=INF;
}
while(1)
{
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
if(FindPath(u)) break;
int dx=INF;
for(i=1; i<=ny; i++)
{
if(!sy[i] && slack[i]<dx)
dx=slack[i];
}
for(i=1; i<=nx; i++)
{
if(sx[i]) lx[i]-=dx;
}
for(i=1; i<=ny; i++)
{
if(sy[i]) ly[i]+=dx;
else slack[i]-=dx;
}
}
}
int sum=0;
for(i=1; i<=ny; i++)
{
if(match[i]!=-1) sum+=mpt[match[i]][i];
}
return sum;
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1; i<=2*n; i++) scanf("%d%d%d%d",&p[i].a,&p[i].b,&p[i].c,&p[i].d);
for(int i = 1; i<=n; i++){
for(int j = 1; j<=n; j++){
int e = 0;
mp.clear();
if(!mp[p[n+j].a]) mp[p[n+j].a]++; else e++;
if(!mp[p[n+j].b]) mp[p[n+j].b]++; else e++;
if(!mp[p[n+j].c]) mp[p[n+j].c]++; else e++;
if(!mp[p[n+j].d]) mp[p[n+j].d]++; else e++;
if(!mp[p[i].a]) mp[p[i].a]++; else e++;
if(!mp[p[i].b]) mp[p[i].b]++; else e++;
if(!mp[p[i].c]) mp[p[i].c]++; else e++;
if(!mp[p[i].d]) mp[p[i].d]++; else e++;
mpt[i][j] = e; //cout<<e<<endl;
}
}
nx = ny = n;
int ans = KM();
printf("%d\n",n*4-ans);
return 0;
}