牛客多校 第五场 二分图匹配 ROOM

作者:东林AotoriChiaki
链接:https://www.nowcoder.com/discuss/90015?type=101&order=0&pos=1&page=1
来源:牛客网

题意:有n个宿舍,每个宿舍住4个人,给定去年n个宿舍的人员构成,今年n个4人组合的情况,求最少几个人需要搬寝室。

思路:转化为最小费用最大流解决的二分图问题,对每个去年的宿舍,向每个今年的组合连一条边,权值为1费用为需要搬的人数(4-相同的人数),源点到去年各点,今年各点到汇点,都连一条权值为1费用为0的最大流,跑一次费用流即可。

mycode:

自己踩的坑:book数组没有初始化

费用流:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
struct node
{
    int u, to, w, c, next;//当前点,目的点,流量,费用,另一个目的点对应Map[]下标
};
#define inf 0x3f3f3f3f
node Map[162000];
int head[160500], vis[500], dist[500], pre[500], cnt;
void addedge(int u, int v, int w, int c)//前向星存图
{
    Map[cnt].u = u;
    Map[cnt].to = v;
    Map[cnt].w = w;//正向为w
    Map[cnt].c = c;//正向为+
    Map[cnt].next = head[u];
    head[u] = cnt++;
    Map[cnt].u = v;
    Map[cnt].to = u;
    Map[cnt].w = 0;//反向为0
    Map[cnt].c = -c;//反向为-
    Map[cnt].next = head[v];
    head[v] = cnt++;
}
int spfa(int s, int e)//求最短路,记录路径
{
    memset(dist, inf, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    queue<int> q;
    q.push(s);
    dist[s] = 0;
    while(!q.empty())
    {
        s = q.front();
        q.pop();
        vis[s] = 0;
        for(int i = head[s]; ~i; i = Map[i].next)
        {
            int to = Map[i].to, w = Map[i].w, c = Map[i].c;
            if(dist[to] > dist[s] + c && w)
            {
                dist[to] = dist[s] + c;
                pre[to] = i;
                if(!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
    if(pre[e] != -1) return 1;
    else return 0;
}
int Min_cost(int s, int e)
{
    int ans = 0;
    while(spfa(s, e))//能找到最短路,也就是增广路
    {
        int max_flow = inf;
        int u = pre[e];
        while(u != -1)
        {
            max_flow = min(max_flow, Map[u].w);//这条增广路,限制流量
            u = pre[Map[u].u];
        }
        u = pre[e];
        while(u != -1)//更新边,和最小费用
        {
            Map[u].w -= max_flow;
            Map[u^1].w += max_flow;
            ans += max_flow * Map[u].c;
            u = pre[Map[u].u];
        }
    }
    return ans;
}
vector<int>da[500],da2[600];
int main()
{
    int n;
    memset(head,-1,sizeof head);
    cnt= 0;
    cin>>n;
    int a,b,c,d;
    for(int i=1; i<=n; i++)
    {
        cin>>a>>b>>c>>d;
        da[i].push_back(a);
        da[i].push_back(b);
        da[i].push_back(c);
        da[i].push_back(d);
        //sort(da[i].begin(),da[i].end());
    }
    for(int i=1; i<=n; i++)
    {
        cin>>a>>b>>c>>d;
        da2[i].push_back(a);
        da2[i].push_back(b);
        da2[i].push_back(c);
        da2[i].push_back(d);
        //sort(da2[i].begin(),da2[i].end());
    }
    int S =0,E = n+n+10;
    for(int i=1; i<=n; i++)
    {
        addedge(S,i,1,0);
    }
    for(int i=1; i<=n; i++)
    {
        addedge(i+n,E,1,0);
    }
    for(int i=1; i<=n; i++)
    {

        for(int p=1; p<=n; p++)
        {
            int cnt =0 ;
            for(int j=0; j<4; j++)
            {
                for(int k=0; k<4; k++)
                {
                    if(da[i][j]==da2[p][k])
                    {
                        cnt++;
                        break;
                    }
                }
            }
          //  cout<<cnt<<endl;
            addedge(i,p+n,1,4-cnt);
        }

    }
    int ans = Min_cost(S,E);
    cout<<ans<<endl;
    return 0;
}

KM算法:

#include<bits/stdc++.h>
using namespace std;
/* KM 算法
* 复杂度 O(nx*nx*ny)
* 求最大权匹配
* 若求最小权匹配,可将权值取相反数,结果取相反数
* 点的编号从 0 开始
*/
const int N = 3010;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数
int g[N][N];//二分图描述
int linker[N],lx[N],ly[N];//y 中各点匹配状态,x,y 中的点标号
int slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
    visx[x] = true;
    for(int y = 0; y < ny; y++)
    {
        if(visy[y])continue;
        int tmp = lx[x] + ly[y] - g[x][y];
        if(tmp == 0)
        {
            visy[y] = true;
            if(linker[y] == -1 || DFS(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if(slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}

int KM()
{
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i = 0; i < nx; i++)
    {
        lx[i] = -INF;
        for(int j = 0; j < ny; j++)
            if(g[i][j] > lx[i])
                lx[i] = g[i][j];
    }
    for(int x = 0; x < nx; x++)
    {
        for(int i = 0; i < ny; i++)
            slack[i] = INF;
        while(true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(DFS(x))break;
            int d = INF;
            for(int i = 0; i < ny; i++)
                if(!visy[i] && d > slack[i])
                    d = slack[i];
            for(int i = 0; i < nx; i++)
                if(visx[i])
                    lx[i] -= d;
            for(int i = 0; i < ny; i++)
            {
                if(visy[i])ly[i] += d;
                else slack[i] -= d;
            }
        }
    }
    int res = 0;
    for(int i = 0; i < ny; i++)
        if(linker[i] != -1)
            res += g[linker[i]][i];
    return res;
}
////HDU 2255
//int main()
//{
//    int n;
//    while(scanf("%d",&n) == 1)
//    {
//        for(int i = 0; i < n; i++)
//            for(int j = 0; j < n; j++)
//                scanf("%d",&g[i][j]);
//        nx = ny = n;
//        printf("%d\n",KM());
//    }
//    return 0;
//}

vector<int>da[500],da2[600];
int main()
{
    int n;
    memset(visx,0,sizeof visx);
    memset(visy,0,sizeof visy);

    cin>>n;

    int a,b,c,d;
    for(int i=0; i<n; i++)
    {
        cin>>a>>b>>c>>d;
        da[i].push_back(a);
        da[i].push_back(b);
        da[i].push_back(c);
        da[i].push_back(d);
        //sort(da[i].begin(),da[i].end());
    }
    for(int i=0; i<n; i++)
    {
        cin>>a>>b>>c>>d;
        da2[i].push_back(a);
        da2[i].push_back(b);
        da2[i].push_back(c);
        da2[i].push_back(d);
        //sort(da2[i].begin(),da2[i].end());
    }

    for(int i=0; i<n; i++)
    {

        for(int p=0; p<n; p++)
        {
            int cnt =0 ;
            for(int j=0; j<4; j++)
            {
                for(int k=0; k<4; k++)
                {
                    if(da[i][j]==da2[p][k])
                    {
                        cnt++;
                        break;
                    }
                }
                g[i][p] = cnt;
                //ag[p][i] = 4-cnt;
            }

            //  cout<<cnt<<endl;

        }

    }
    nx  =ny =n;
    int ans = KM();
    cout<<4*n-ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/axuhongbo/article/details/82048012