首页 > 其他分享 >P1559 运动员最佳匹配问题 题解

P1559 运动员最佳匹配问题 题解

时间:2022-09-18 22:24:50浏览次数:95  
标签:匹配 int 题解 期望值 标杆 算法 P1559 include

本篇使用 \(\text{KM}\) 算法求解。

对于 \(\text{KM}\) 算法

步骤如下:

首先要用邻接矩阵存二分图,然后用贪心算法初始化标杆,运用匈牙利算法找到完美匹配,如果找不到则修改标杆增加一些边。重复以上步骤直到找到完美匹配。

标杆分为 \(X\) 标杆和 \(Y\) 标杆,一般把比较少的点放在 \(X\) 标杆一侧。

首先要初始化两个标杆分别为 \(X\) 标杆和 \(Y\) 标杆,\(X\) 标杆初始化为与之相连的最大边权,\(Y\) 标杆初始化为 \(0\),且直接加入拥有最大边权的边。如果发现此时的匹配就是完美匹配,那么就直接退出,否则进行标杆的更改。从第一个节点开始扫描,如果有合法的增广路,那么将其反选,扩充路径,如果该节点没有合法的增广路,那么则将增广路上的所有的 \(X\) 标杆上的点加入点集 \(S\),将 \(Y\) 标杆上的所有点加入点集 \(T\),从 \(S\) 和不在 \(T\) 集合中的点里面,计算 \(d=\min \left \{ L(x)+L(y)-w(x,y) \right \}\) 计算后,将在 \(S\) 点集内 \(x\) 的顶标减 \(d\),在 \(T\) 的顶标加 \(d\)。并将目前没有加入二分图的权值和等于顶标和的边作为未匹配边加入到二分图中,然后再在该节点寻找增广路,如果还是没有,则再次通过更改标杆来增加边,直到有增广路出现为止。之后重复寻找增广路的步骤以及更改标杆的步骤,如果出现了完美匹配就直接退出。

本人的一点人话解释:

\(\text{KM}\) 算法一般用于解决寻找最优匹配的问题,是基于匈牙利算法的一种算法,和匈牙利相比,他多了个左右期望值。

左期望值的初值为它所连接的边的最大边权,右期望值的初值为 0 。如果左右期望值加起来的值等于边权的话,即匹配成功,否则不匹配,匹配失败的话我们需要 dfs 找出最小的能使其匹配成功的期望值,并将参与 dfs 的左期望值减去搜到的值,右期望值加上搜到的值并继续匹配。(没看明白的话请看下面大佬的博客理解吧,\(lz\) 水平实在有限 \(QAQ\) )。

\(\text{KM}\) 算法详解,请点这里,同时内附匈牙利算法链接。

本题思路

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

\(Code\)

#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;
}

标签:匹配,int,题解,期望值,标杆,算法,P1559,include
From: https://www.cnblogs.com/Alwaysmaxx/p/16706016.html

相关文章

  • 串的模式匹配算法
    一、算法设计思想1.简单模式匹配算法从主串的第一个位置开始和模式串的第一个字符开始比较,相等继续比较下一个字符;否则从主串的下一个字符和模式串的第一个字符重新开始......
  • SWERC 2021-2022 部分简要题解
    比赛链接:https://codeforc.es/contest/1662。前言「部分」「简要」题解。A-OrganizingSWERC直接判断。C-EuropeanTrip如果不考虑限制,我们可以直接矩乘。考虑......
  • 【题解】CF1311E Construct the Binary Tree
    题目链接-->Problem-E-Codeforces题目大意给定\(n\)和\(d\),你需要构造一棵\(n\)个点的二叉树满足所有点的深度之和恰好为\(d\)。\(2≤n,d≤5000\)。分析......
  • CSP2021-s题解
    T1廊桥分配题目链接P7913[CSP-S2021]廊桥分配\(Lemma:\)对于已经存在的廊桥集合,加入新的廊桥不会影响之前分配好的航班。证明:由于题目所给限制,若有空廊桥,则航......
  • Luogu P3674 小清新人渣的本愿 题解
    P3674小清新人渣的本愿lxl大爷的题,%%%%%以及CSPrp++!!!前言:其实是这题的名字吸引了我,毕竟人渣的本愿里的角色我觉得多多少少都沾点,哪来的小清新啊。《人渣的本愿》,又名......
  • 牛客题解 约瑟夫环
    链接:https://ac.nowcoder.com/acm/problem/22227来源:牛客网题解作者岛田小雅正统的约瑟夫环我不会,看了一眼范围直接开始乱搞了。我选择的方法是模拟,用递归函数实现(虽......
  • 2022上半年软件设计师真题解析
    选择题 ......
  • AtCoder Beginner Contest 269 (A-F)题解
    A-AnywayTakahashi这里我还是关了ll的C开了忘了关害的F多了一发罚时#include<bits/stdc++.h>usingnamespacestd;constintN=3e5+10;constintM=9982443......
  • 题解【CF1702G2 Passable Paths (hard version)】
    题目传送门。考虑建立虚树然后再上面搞树形DP。于是这道题就变成分讨题。设\(f_i\)表示\(i\)子树内的答案。若\(f_i=1\),表示\(i\)子树内的特殊点可以被一条链覆......
  • CF1718D 题解
    设\(k\)为\(a\)中的空位数量。首先咱们转化这个“相似”的条件,发现它其实是说,笛卡尔树的结构相同。那么我们把p建笛卡尔树然后把a的数往上填。如果此时有上面小于下......