『题解』 运动员最佳匹配问题

本篇使用 \(\text{KM}\) 算法求解。
如果你不知道什么是 \(\text{KM}\) 算法,清点这里

思路

我们可以知道将 \(i\) 号男运动员与 \(j\) 号女运动员匹配所得到的竞赛优势为 \(P[i][j]\times Q[j][i]\) (如果你不知道为什么是
\(Q[j][i]\) 的话请仔细看题),只需要将他们的优势用邻接矩阵存起来在跑 \(\text{KM}\) 即可。(代码附详细注释)

\(Code\)

ヾ(≧▽≦*)o
#include<bits/stdc++.h>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define gc getchar
#include<algorithm>
#define reg register
#define ll long long
//#define int long long
using namespace std;
const int M=25;
const int N=1e5+5;
const int mod=998244353;
//const int INF = 0x3f3f3f3f;为了防止ctrl将这里注释掉了
inline void print(int x) {if (x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0');}
inline int read() { int res = 0, f = 0; char ch = gc();for (; !isdigit(ch); ch = gc()) f |= (ch == '-'); for (; isdigit(ch); ch = gc()) res = (res << 1) + (res << 3) + (ch ^ '0'); return f ? -res : res;}

int n,w,ans,mini;
int p[M],vis1[M],vis2[M],L[M],R[M],a[M][M]; 
//p数组记录匹配对象,vis1vis2分别标记左右部分的点,LR是左右期望值,a是处理边权的 
inline int dfs(int u)//dfs匹配左右点 
{
    vis1[u]=1;//标记左半部分的点已经连接 
    for(reg int i=1;i<=n;i++)//遍历 
    {
        if(!vis2[i])//如果右半部分的点没有被访问过 
        {
            int x=L[u]+R[i]-a[u][i];//计算期望值和好感度的差 
            if(x==0)//如果结果为0,即期望值等于好感度 
            {
                vis2[i]=1;//标记右节点 
                if(!p[i] || dfs(p[i]))//如果当前点没有与之匹配的点或者当前点所匹配的点还可与其他节点相匹配 
                {
                    p[i]=u;//匹配成功 
                    return 1;//返回成功 
                }
            }
            else if(x>0)//如果期望值不等于好感值,则记录最小的差 
            mini=min(mini,x); 
        }
    }
    return 0;//匹配失败 
}

inline void KM()
{
    for(reg int i=1;i<=n;i++)
    {
        while(1)
        {
            mini=INF;//重置最小值 
            memset(vis1,0,sizeof(vis1));
            memset(vis2,0,sizeof(vis2));//重置vis数组 
            if(dfs(i)) break;//匹配成功了就退出 
            for(reg int j=1;j<=n;j++) if(vis1[j]) L[j]-=mini;//左期望值减去最小值 
            for(reg int j=1;j<=n;j++) if(vis2[j]) R[j]+=mini;//右期望值加上最小值 
        }
    }
}

signed main()
{
    n=read();
    for(reg int i=1;i<=n;i++)
        for (reg int j=1;j<=n;j++)        
            a[i][j]=read();//输入 
    for(reg int i=1;i<=n;i++)
        for(reg int j=1;j<=n;j++)
            w=read(),a[j][i]*=w; //求出边权 
    for(reg int i=1;i<=n;i++)
        for(reg int j=1;j<=n;j++)
            L[i]=max(a[i][j],L[i]);//求出左期望值 
    KM();//跑一遍KM算法 
    for(reg int i=1;i<=n;i++)
    ans+=a[p[i]][i];//累加答案 
    printf("%d",ans);
    return 0;
}
posted @ 2022-07-26 17:34  Always_maxx  阅读(137)  评论(0)    收藏  举报