带权二分图匹配(最小费用最大流) 8.2牛客暑期多校训练营五 E

E、room

| 时间限制:1 秒 | 内存限制:256M

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<=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.

示例 1

输入

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

输出

2

说明

Just swap 4 and 5

题目大意:

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

想法:

​ 比赛的时候想着贪心地把原寝室抱团人最多的不搬来算,WA了。然后看题解说可以变成带权二分图匹配问题。。。原来是费用流啊,填坑填坑。

思路:

参照标准题解:

假设新的宿舍里,第 i 个4人团体安排到第 j 间宿舍,那么不用搬的学生数量, 就是4人团体和原住民的交。变成带权二分图匹配问题,可以用费用流做 。

原寝室和新组队分别为两个图中的点。点之间连线的费用就是两个点之间的匹配度。

AC代码:

题解:

搬自牛客网讨论区

作者:东林AotoriChiaki
链接:https://www.nowcoder.com/discuss/90015?type=101&order=0&pos=1&page=0
来源:牛客网
​
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
 
const int MAXN = 205;
const int MAXM = 40005;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;
void init(int n)                     
{
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
    edge[tol].to = v;               //正向边 
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];       //边tol的下一条边为上一个头为u的边 
    head[u] = tol++;                //更新头为u的边为现在的边tol   
    edge[tol].to = u;               //反向边 
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    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 = edge[i].next)         //遍历头为u的边 
        {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost )        //边容量没用完,且费用可松弛,则更新 
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;                                 //记录最小费用流到点v的边为i 
                if(!vis[v])                                 //v不在队则入队 
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;                           //没有流到终点t的边,则说明增广失败  
    else return true;
}
​
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[edge[i^1].to])         //找到最小费用增广路上的最小权值,i^1取奇数(正向边) 
        {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])         //更新残量网络 
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}
int n;
struct node{
int a,b,c,d;
}r[205];
int cou(int x,int y)    //费用为两点间不相同人的个数,也可以用set 
{
    int res=4;
    res-=(r[x].a==r[y].a||r[x].a==r[y].b||r[x].a==r[y].c||r[x].a==r[y].d);       
    res-=(r[x].b==r[y].a||r[x].b==r[y].b||r[x].b==r[y].c||r[x].b==r[y].d);
    res-=(r[x].c==r[y].a||r[x].c==r[y].b||r[x].c==r[y].c||r[x].c==r[y].d);
    res-=(r[x].d==r[y].a||r[x].d==r[y].b||r[x].d==r[y].c||r[x].d==r[y].d);
    return res;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=2*n;i++)
    {
        scanf("%d%d%d%d",&r[i].a,&r[i].b,&r[i].c,&r[i].d);
    }
    init(2*n+2);
    for (int i=1;i<=n;i++)
    {
        addedge(0,i,1,0);               //二分图左半边,容量都为1 
        addedge(n+i,2*n+1,1,0);         //二分图右半边 
        for (int j=n+1;j<=2*n;j++)
        {
            addedge(i,j,1,cou(i,j));    //二分图之间连线,要计算费用 
        }
    }
    int ans;
    minCostMaxflow(0,2*n+1,ans);
    printf("%d\n",ans);
    return 0;
}
 
    
​


猜你喜欢

转载自blog.csdn.net/KeeeepGO/article/details/81487353