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

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

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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间宿舍,每一间有4n个学生,第一年学校安排住宿方式

第二年换宿舍是学生自己组合的,

现在给出第一年和第二年的住宿方式,问换宿舍人数最少是多少。

思路:

一开始想用并查集记录有多少最多有多少学生不用换宿舍,但是如果有两个宿舍都最多有2个人不用换宿舍,但是这两组人是住在同一个宿舍的话,并查集的做法就会出问题

例如给出

3
1 2 3 4
5 6 7 8
9 10 11 12


1 2 7 8
3 5 11 12
4 6 9 10

并查集的做法是6个人,但是实际上是7个人。

也就是说不能单纯根据最后捆绑的人越多就不动,因为宿舍与宿舍之间也是有联系的。

想到组与组之间有联系的话就突然想到这其实就是一个带权二分图的匹配问题。

这里我的做法是反过来想,要使得最少的人改变宿舍,就相当于使最多的人不改变宿舍

那么我们对第一年的宿舍i和第二年的宿舍j连边,权值为宿舍i变成宿舍j最多能保留多少个学生。

对这个二分图求最大匹配得出的权值就是最多有多少学生不改变宿舍

这个结果用4n减去就是所求答案

这里我用的是km模板,当然也是可以用费用流怼过去的。

#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#include<cstdio>
#include<functional>
#include<iomanip>
#include<cmath>
#include<stack>
#include<iomanip>
#include<functional>
#include<iomanip>
#include<bitset>
#define lson l,m
#define rson m+1,r
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 111;
const int BN = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = 998244353;
const double eps = 1e-8;
int mp1[maxn][5], mp2[maxn][5];
struct KM {
    int link[maxn], lx[maxn], ly[maxn], stacks[maxn]; //lx,ly为顶标,nx,ny分别为x点集y点集的个数
    int mp[maxn][maxn], ny, nx, n;
    bool visx[maxn], visy[maxn];
    void init(int _nx, int _ny, int _n) {
        nx = _nx, ny = _ny, n = _n;
    }
    bool dfs(int x) {
        visx[x] = 1;
        for (int i = 1; i <= ny; i++) {
            if (visy[i]) continue;
            int tmp = lx[x] + ly[i] - mp[x][i];
            if (tmp == 0) {
                visy[i] = 1;
                if (link[i] == -1 || dfs(link[i])) {
                    link[i] = x;
                    return true;
                }
            }
            else if (stacks[i] > tmp) stacks[i] = tmp;
        }
        return false;
    }
    int solve() {
        memset(link, -1, sizeof(link));
        memset(ly, 0, sizeof(ly));
        int i, j;
        for (i = 1; i <= nx; i++)
            for (j = 1, lx[i] == -inf; j <= ny; j++)
                if (mp[i][j] > lx[i])
                    lx[i] = mp[i][j];
        for (i = 1; i <= nx; i++) {
            for (j = 1; j <= ny; j++)
                stacks[j] = inf;
            while (1) {
                memset(visx, 0, sizeof(visx));
                memset(visy, 0, sizeof(visy));
                if (dfs(i)) break;
                int d = inf;
                for (j = 1; j <= ny; j++)
                    if (!visy[j] && d > stacks[j])
                        d = stacks[j];
                for (j = 1; j <= nx; j++)
                    if (visx[j])
                        lx[j] -= d;
                for (j = 1; j <= ny; j++) {
                    if (visy[j]) ly[j] += d;
                    else stacks[j] -= d;
                }
            }
        }
        int ans = 0;
        for (i = 1; i <= ny; i++)
            if (link[i] != -1)
                ans += mp[link[i]][i];
        return ans;
    }
}km;
int main() {
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //freopen("D:\\cpp\\test.txt", "r", stdin);
    //freopen("D:\\cpp\\\comper\\8.in", "w", stdout);
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= 4; j++)
                scanf("%d", &mp1[i][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= 4; j++)
                scanf("%d", &mp2[i][j]);
        km.init(n, n, n);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                int cnt = 0;
                for (int k = 1; k <= 4; k++) {
                    for (int l = 1; l <= 4; l++) {
                        if (mp1[i][k] == mp2[j][l]) {
                            cnt++;
                            break;
                        }
                    }
                }
                km.mp[i][j] = cnt;
            }
        }
        printf("%d\n", 4 * n - km.solve());
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值