牛客网暑期ACM多校训练营(第五场)E room(费用流)

链接: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个宿舍,四人一个宿舍,给出原来哪四个人在一个宿舍和现在哪四个人想住一个宿舍。求最少多少人要搬宿舍。

思路:明显就是费用流啊!!!刚开始想到了结果没继续想就去看A题了!!!

首先,起点s=0向每个原先的宿舍i连边,流量为1,费用为0,每个后来的宿舍i+n向终点t=2*n+1连边,流量为1,费用为0。

然后,两种建边方法。如果以换宿舍的人作为费用的话,只需要求每个宿舍i和现在的宿舍j中有 多少个不同的人 作为费用建边,流量为1,答案就是最大流时的总费用。如果以不换宿舍的人建边的话,要使在原来宿舍的人越多的宿舍先不换宿舍,那么每个宿舍i和现在的宿舍j中有多少个相同的人,建边(i,j+n)费用就是 -相同的人数 ,流量为1。答案就是4*n+最大流的总费用。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1010;
const int MAXM = 100010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct Edge
{
    int to, next, cap, flow, cost;
} edge[MAXM];
int head[MAXN], tol;
int pre[MAXN], dis[MAXN];
bool vis[MAXN];
int N,n,m,k;//节点总个数,节点编号从0~N-1
void add(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];
    head[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)
        {
            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;
                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[edge[i ^ 1].to])
        {
            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 sum,sumin,cnt;
int g[MAXN][MAXN],in[MAXN][MAXN];
vector<int>vc[MAXM];
void dfs(int cnt,int u)
{
    vc[cnt].push_back(u);
    in[cnt][u]=1;
    for(int i=1;i<=n;i++)
    {
        if(!g[u][i]||in[cnt][i]) continue;
        dfs(cnt,i);
    }
}
void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}
int pr[MAXN][4],ne[MAXN][4];
int cal(int x,int y)
{
    int a=0;
    for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
    if(pr[x][i]==ne[y][j]) a++;
    return a;
}
void build(int s,int t)
{
    for(int i=1;i<=n;i++)
    {
        add(s,i,1,0);
        add(i+n,t,1,0);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        int k=cal(i,j);
        add(i,j+n,1,-k);
    }
}
int main(){
    int Tc,cas=1,S,T;
      while(scanf("%d",&n)!=EOF)
      {
          for(int i=1;i<=n;i++)
          for(int j=0;j<4;j++)
          scanf("%d",&pr[i][j]);
          for(int i=1;i<=n;i++)
          for(int j=0;j<4;j++)
          scanf("%d",&ne[i][j]);
          init();
          S=0;
          T=n+n+1;
          N=T+1;
          build(S,T);
          int ct=0;
          int ans=minCostMaxflow(S,T,ct);
          printf("%d\n",4*n+ct);
        }
return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值