zoukankan      html  css  js  c++  java
  • Luogu 1559 运动员最佳匹配问题(带权二分图最大匹配)

    Luogu 1559 运动员最佳匹配问题(带权二分图最大匹配)

    Description

    羽毛球队有男女运动员各n人。给定2 个n×n矩阵P和Q。P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞赛优势;Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势。由于技术配合和心理状态等各种因素影响,P[i][j]不一定等于Q[j][i]。男运动员i和女运动员j配对组成混合双打的男女双方竞赛优势为P[i][j]*Q[j][i]。设计一个算法,计算男女运动员最佳配对法,使各组男女双方竞赛优势的总和达到最大。

    Input

    第一行有1 个正整数n (1≤n≤20)。接下来的2n行,每行n个数。前n行是p,后n行是q。

    Output

    将计算出的男女双方竞赛优势的总和的最大值输出。

    Sample Input

    3
    10 2 3
    2 3 4
    3 4 5
    2 2 2
    3 5 3
    4 5 1

    Sample Output

    52

    Http

    Luogu:https://www.luogu.org/problem/show?pid=1559#sub

    Source

    带权二分图的最大匹配

    解决思路

    这个题目是一道带权二分图最大匹配的题目,我们要用到的就是KM算法。

    那么什么是KM算法呢?

    通俗点来讲,KM算法就是给匈牙利算法提供一张图,让后者去匹配。

    KM算法是基于贪心的思想,选取权值最大的边建图,然后让匈牙利算法去判断其可行性。

    我们知道,匈牙利算法的关键就是“让”出边来匹配,那么KM算法的关键就是如何建出这张图。

    接下来我们结合代码来看一看。

    首先我们对代码中的变量进行一下说明,我们定义二分图的左边这一组为
    X,右边一组为Y。定义use_left,use_right分别标记左边或右边的点是否标记,同时标记其是否在增广路中。定义Wx,Wy分别为左边和右边的顶标这是KM算法的关键之处。这个顶标是用来干嘛的呢?这就是用来判断某一条边是否在我们定义的图中的。我们定义只有满足Wx[i]+Wy[j]==W[i][j]的这样一条边i->j才在图中存在。所以初始化的时候,Wx[i]置为从i出发的最大边的权值,而Wy[j]置为0。这就相当于把i->j这条边加入图中。

    对于每一个左边的点i,我们首先对其跑一边匈牙利算法,如果发现无法匹配,则进行下面的操作。从剩余的边中选一条出发点i已标记而到达点j未标记(标记操作在匈牙利算法中实现),并满足Wx[i]+Wy[j]-W[i][j]最小。选择这条边,然后把所有已标记的左边的点的Wx加上W[i][j],而右边的已标记的点的Wy分别减去W[i][j]。这相当于把i->j这条边加入到我们要判断的图中,并且原来图中有的边不变。(仔细想一想,结合我们判断某条边是否在图中的方法:Wx[i]+Wy[j]==W[i][j])

    推荐一篇关于二分图匹配的KM算法的文章:http://www.cnblogs.com/Lanly/p/6291214.html

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    #define mem(Arr,x) memset(Arr,x,sizeof(Arr))
    
    class EDGE
    {
    public:
        int v,w;
    };
    
    const int maxN=30;
    const int inf=2147483647;
    
    int n;
    vector<EDGE> E[maxN];//存二分图的边
    int Match[maxN];//存右边匹配的左点编号
    bool use_left[maxN];//判断是否访问,一是避免重复,二十判断是否在增广路上,以便后面加边
    bool use_right[maxN];
    int Wx[maxN];//两边的顶标
    int Wy[maxN];
    
    int read();
    bool Hungary(int u);
    
    int main()
    {
        int P[maxN][maxN];
        int Q[maxN][maxN];
        mem(Match,-1);
        mem(Wy,0);
        cin>>n;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                P[i][j]=read();
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                Q[i][j]=read();
        for (int i=1;i<=n;i++)
        {
            int D=-inf;//初始化左边的顶标
            for (int j=1;j<=n;j++)
            {
                E[i].push_back((EDGE){j,P[i][j]*Q[j][i]});
                D=max(D,P[i][j]*Q[j][i]);//预处理出Wx
            }
            Wx[i]=D;
            Wy[i]=0;
        }
    
        for (int i=1;i<=n;i++)
        {
            do
            {
                mem(use_left,0);
                mem(use_right,0);
                if (Hungary(i))
                    break;
                int D=inf;
                for (int j=1;j<=n;j++)
                    if (use_left[j]==1)
                    {
                        for (int k=0;k<E[j].size();k++)
                        {
                            int v=E[j][k].v;
                            int w=E[j][k].w;
                            if (use_right[v]==0)
                            {
                                D=min(D,Wx[j]+Wy[v]-w);
                            }
                        }
                    }
                for (int j=1;j<=n;j++)
                    if (use_left[j])
                        Wx[j]=Wx[j]-D;
                for (int j=1;j<=n;j++)
                    if (use_right[j])
                        Wy[j]=Wy[j]+D;
            }
            while (1);
        }
        int Ans=0;
        for (int i=1;i<=n;i++)
            for (int j=0;j<E[Match[i]].size();j++)
            {
                if (E[Match[i]][j].v==i)
                {
                    Ans+=E[Match[i]][j].w;
                    break;
                }
            }
        cout<<Ans<<endl;
        return 0;
    }
    
    int read()//读入优化
    {
        int x=0;
        int k=1;
        char ch=getchar();
        while (((ch>'9')||(ch<'0'))&&(ch!='-'))
            ch=getchar();
        if (ch=='-')
        {
            k=-1;
            ch=getchar();
        }
        while ((ch<='9')&&(ch>='0'))
        {
            x=x*10+ch-48;
            ch=getchar();
        }
        return x*k;
    }
    
    bool Hungary(int u)//匈牙利算法判断
    {
        use_left[u]=1;
        for (int i=0;i<E[u].size();i++)
        {
            int v=E[u][i].v;
            if ((use_right[v]==0)&&(Wx[u]+Wy[v]==E[u][i].w))
            {
                use_right[v]=1;
                if ((Match[v]==-1) || (Hungary(Match[v])) )
                    {
                        Match[v]=u;
                        return 1;
                    }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    POJ 3713 Transferring Sylla【Tarjan求割点】
    Tarjan算法【强连通分量】
    POJ 1273 Drainage Ditches【最大流模版】
    CDOJ 1960 构造哈密顿路径
    HDU 1384 Intervals【差分约束-SPFA】
    POJ 1364 / HDU 3666 【差分约束-SPFA】
    【SPFA与Dijkstra的对比】CDOJ 1961 咸鱼睡觉觉【差分约束-负权最短路径SPFA】
    CDOJ 1965 连通域统计【DFS】
    CDOJ 1964 命运石之门【最短路径Dijkstra/BFS】
    最小生成树模板【kruskal & prim】
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7138223.html
Copyright © 2011-2022 走看看