牛客网暑期ACM多校训练营(第五场) - E room (二分图匹配 费用流)

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值