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

链接:https://www.nowcoder.com/acm/contest/143/E
来源:牛客网
 

题目描述

Nowcoder University has 4n students and n dormitories ( Four students per dormitory). Students numbered from 1 to 4n.

And in the first year, the i-th dormitory 's students are (x1[i],x2[i],x3[i],x4[i]), now in the second year, Students need to decide who to live with.

In the second year, you get n tables such as (y1,y2,y3,y4) denote these four students want to live together.

Now you need to decide which dormitory everyone lives in to minimize the number of students who change dormitory.

输入描述:

The first line has one integer n.

Then there are n lines, each line has four integers (x1,x2,x3,x4) denote these four students live together in the first year

Then there are n lines, each line has four integers (y1,y2,y3,y4) denote these four students want to live together in the second year

输出描述:

Output the least number of students need to change dormitory.

示例1

输入

2
1 2 3 4
5 6 7 8
4 6 7 8
1 2 3 5

输出

2

说明

Just swap 4 and 5

备注:

1<=n<=100

1<=x1,x2,x3,x4,y1,y2,y3,y4<=4n

It's guaranteed that no student will live in more than one dormitories.

题意:一间学校有n 间宿舍,每间宿舍有4 个人,给出这个学校第一学年的宿舍安排,现在第二学年需要换寝室,换寝室是学生自己组队,每四个人抱团,现在你需要给他们安排具体的宿舍位置,使得换宿舍的人数量尽可能少

思路:一直以为是个暴力贪心的题,疯狂分类讨论,一直没过,结果是个带权二分图匹配问题。第一年n个宿舍的安排,第二年n个宿舍的安排,分别作为左右两边点集,左边第i个宿舍与右边第j个宿舍连边,权值为i宿舍与j宿舍人员变化数量(如1 2 3 4变为1 2 3 5,权值就是1),然后用KM跑一遍即可。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int qwq=0x7fffffff;
int w[1000][1000];  //w数组记录边权值
int line[1000],usex[1000],usey[1000],cx[1000],cy[1000];  //line数组记录右边端点所连的左端点, usex,usey数组记录是否曾访问过,也是判断是否在增广路上,cx,cy数组就是记录点的顶标
int n,ans,m;  //n左m右
bool find(int x){
    usex[x]=1;
    for (int i=1;i<=m;i++){
        if ((usey[i]==0)&&(cx[x]+cy[i]==w[x][i])){   //如果这个点未访问过并且它是子图里面的边
            usey[i]=1;
            if ((line[i]==0)||find(line[i])){   //如果这个点未匹配或者匹配点能更改
                line[i]=x;
                return true;
            }
        }
    }
    return false;
}
int km(){
    for (int i=1;i<=n;i++){  //分别对左边点依次匹配
        while (true){
            int d=qwq;
            memset(usex,0,sizeof(usex));
            memset(usey,0,sizeof(usey));
            if (find(i)) break;  //直到成功匹配才换下一个点匹配
            for (int j=1;j<=n;j++){
                if (usex[j]){
                    for (int k=1;k<=m;k++)
                    if (!usey[k]) d=min(d,cx[j]+cy[k]-w[j][k]);  //计算d值
                }
            }
            if (d==qwq) return -1;
            for (int j=1;j<=n;j++)
                if (usex[j]) cx[j]-=d;
            for (int j=1;j<=m;j++)
                if (usey[j]) cy[j]+=d;     //添加新边
        }
    }
    ans=0;
    for (int i=1;i<=m;i++)
    ans+=w[line[i]][i];
    return ans;
}
int main(){
	scanf("%d",&n);
	int a[110][5];
	int b[110][5];
	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]);
		}
	}
	memset(cy,0,sizeof(cy));
    memset(w,0,sizeof(w));
    memset(cx,0,sizeof(cx));
	for(int i=1;i<=n;i++){
		int d=0;
		for(int j=1;j<=n;j++){
			int sum=0;
			for(int k=1;k<=4;k++){
				int flag=0;
				for(int p=1;p<=4;p++){
					if(a[i][k]==b[j][p])flag=1;
				}
				if(!flag)sum++;
			}
			w[i][j]=-sum;
			d=max(d,w[i][j]);
		}
		cx[i]=d;
	}
	m=n;
    memset(line,0,sizeof(line));
    printf("%d\n",-km());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yz467796454/article/details/81366469