https://www.nowcoder.com/acm/contest/143/E
题意:
给你n个寝室。每个寝室有4个人。
给你第一年的n个寝室的人员分配。再给你第二年的n个寝室的人员分配。
问你最少要让几个人换寝室。不是次数,是人数。
POINT:
把新旧寝室分成二分图。旧寝室到新寝室要搬走的人数很好处理。
这样就是n*n条边的二分图,有权值。 这样就是二分图最小权值匹配。
用最小费用最大流做。
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;
#define LL long long
const int N = 500+3;
const int inf = 0x3f3f3f3f;
struct edge
{
int from,to,cap,flow,cost;
edge(int u,int v,int c,int f,int dd):
from(u),to(v),cap(c),flow(f),cost(dd){}
};
vector<int>G[N];
vector<edge>len;
int s,t;
void add(int u,int v,int cap,int cost)
{
len.push_back(edge(u,v,cap,0,cost));
len.push_back(edge(v,u,0,0,-cost));
G[u].push_back(len.size()-2);
G[v].push_back(len.size()-1);
}
bool spfa(int &ans)
{
int dis[N],pre[N],inq[N];
for(int i=0;i<=t;i++) dis[i]=inf,inq[i]=0,pre[i]=-1;
dis[0]=0;pre[0]=0;inq[0]=1;
int a=inf;
queue<int> q;
q.push(0);
while(!q.empty()){
int u = q.front();q.pop();inq[u]=0;
for(int i=0;i<G[u].size();i++){
edge e=len[G[u][i]];
if(e.cap>e.flow&&dis[e.to]>dis[u]+e.cost){
dis[e.to]=dis[u]+e.cost;
a=min(a,e.cap-e.flow);
pre[e.to]=G[u][i];
if(!inq[e.to]){
q.push(e.to);
inq[e.to]=1;
}
}
}
}
if(dis[t]==inf) return 0;
ans+=dis[t];
int u = t;
while(u!=s){
len[pre[u]].flow+=a;
len[pre[u]^1].flow-=a;
u=len[pre[u]].from;
}
return 1;
}
int mincost()
{
int cost=0;
while(spfa(cost)){
}
return cost;
}
int a[N][5];
int b[N][5];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=4;j++)
scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=4;j++)
scanf("%d",&b[i][j]);
}
for(int i=1;i<=n;i++){
add(0,i,1,0);
add(i+n,2*n+1,1,0);
for(int j=1;j<=n;j++)//void add(int u,int v,int cap,int cost)
{
int num=0;
for(int k=1;k<=4;k++){
int flag=1;
for(int kk=1;kk<=4;kk++){
if(a[i][k]==b[j][kk]){
flag=0;
break;
}
}
if(flag)
num++;
}
add(i,j+n,1,num);
}
}
s=0,t=2*n+1;
printf("%d\n",mincost());
return 0;
}