牛客多校第五场 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

题目大意:学校里一共有n个宿舍,每个宿舍里有4个学生。给出一开始学生的住宿情况和第二学期里学生们所想要的宿舍分配情况,问如果要满足第二学期所有学生要求的宿舍分配情况,最少有多少个学生要搬宿舍。

题目思路:对于每个新的宿舍的住宿情况,如果是原来第 i 个宿舍要变为第 j 个宿舍,要搬动的人数就是4 - 在第 j 个宿舍中原来是在第 i 个宿舍的人数数量,这样第 i 个宿舍都可以向每一个新的宿舍建一条流量为1,费用为(4 - 在第 j 个宿舍中原来是在第 i 个宿舍的人数数量)的边,再建立一个源点和汇点,源点向原来的宿舍建一条流量为1,费用为0的边,新的宿舍往汇点建一条流量为1,费用为0的边。接着再跑一遍费用流即可。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lowbit(x) x&-x
#define pb push_back
#define MP make_pair
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef vector<int> VI;
 
const int MX = 500 + 5;//都开 4 倍把..
const int MM = 4*MX*MX;
const int INF = 0x3f3f3f3f;
struct Edge {
    int to, next, cap, flow, cost;
    Edge() {}
    Edge(int _to, int _next, int _cap, int _flow, int _cost) {
        to = _to;
        next = _next;
        cap = _cap;
        flow = _flow;
        cost = _cost;
    }
} E[MM];
int Head[MX], tol;
int pre[MX]; //储存前驱顶点
int dis[MX]; //储存到源点 s 的距离
bool vis[MX];
int N;//节点总个数,节点编号从 0~N-1
void init(int n) {
    tol = 0;
    N = n + 2;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v, int cap, int cost) {
    E[tol] = Edge(v, Head[u], cap, 0, cost);
    Head[u] = tol++;
    E[tol] = Edge(u, Head[v], 0, 0, -cost);
    Head[v] = tol++;
}
bool spfa(int s, int t) {
    queue<int>q;
    for (int i = 0; i < N; i++) {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = Head[u]; i != -1; i = E[i].next) {
            int v = E[i].to;
            if (E[i].cap > E[i].flow && dis[v] > dis[u] + E[i].cost) {
                dis[v] = dis[u] + E[i].cost;
                pre[v] = i;
                if (!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if (pre[t] == -1) return false;
    else return true;
}
//返回的是最大流, cost 存的是最小费用
int minCostMaxflow(int s, int t, int &cost) {
    int flow = 0;
    cost = 0;
    while (spfa(s, t)) {
        int Min = INF;
        for (int i = pre[t]; i != -1; i = pre[E[i ^ 1].to]) {
            if (Min > E[i].cap - E[i].flow)
                Min = E[i].cap - E[i].flow;
        }
        for (int i = pre[t]; i != -1; i = pre[E[i ^ 1].to]) {
            E[i].flow += Min;
            E[i ^ 1].flow -= Min;
            cost += E[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}
 
int pos[MX],x[5];
 
int main(){
    //FIN;
    int n;scanf("%d",&n);
    int S=0,T=2*n+1;N=2*n+2;
    init(3*n);
    for(int i=1;i<=n;i++){
        edge_add(S,i,1,0);
        for(int j=1;j<=4;j++){
            int p;scanf("%d",&p);
            pos[p]=i;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=4;j++){
            scanf("%d",&x[j]);
        }
 
        for(int j=1;j<=n;j++){
            int c=0;
            for(int k=1;k<=4;k++){
                if(pos[x[k]]!=j) c++;
            }
            edge_add(j,i+n,1,c);
        }
    }
    for(int i=1;i<=n;i++) edge_add(i+n,T,1,0);
    int cost;
    minCostMaxflow(S,T,cost);
    printf("%d\n",cost);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/81366954