KM

本文介绍了一种解决运动员最佳配对问题的算法,旨在通过计算男女运动员的竞赛优势,找到最佳的混合双打组合,使整体竞赛优势总和最大化。算法采用匈牙利算法和KM算法实现,适用于羽毛球队或其他体育团队的配对问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运动员最佳匹配问题

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem 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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=35;
const int inf=0x3f3f3f3f;
int n;
int w[maxn][maxn];
int lx[maxn], ly[maxn];
int match[maxn];
int slack[maxn];
bool s[maxn], t[maxn];
bool dfs(int i)
{
    s[i]=1;
    for(int j=1; j<=n; j++)
    {
        int cnt=lx[i]+ly[j]-w[i][j];
        if(cnt==0&&!t[j])
        {
            t[j]=1;
            if(!match[j]||dfs(match[j]))
            {
                match[j]=i;
                return 1;
            }
        }
        else
        {
            slack[j]=min(slack[j], cnt);
        }
    }
    return 0;
}
void updata()
{
    int a=inf;
    for(int i=1; i<=n; i++)
    {
        if(!t[i])
            a=min(a, slack[i]);
    }
    for(int i=1; i<=n; i++)
    {
        if(s[i])
            lx[i]-=a;
        if(t[i])
            ly[i]+=a;
    }
}
void km()
{
    memset(match, 0, sizeof(match));
    memset(lx, 0, sizeof(lx));
    memset(ly, 0, sizeof(ly));
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            lx[i]=max(lx[i], w[i][j]);
        }
    }
    for(int i=1; i<=n; i++)
    {
        memset(slack, 0x3f, sizeof(slack));
        while(1)
        {
            memset(s, 0, sizeof(s));
            memset(t, 0, sizeof(t));
            if(dfs(i))
                break;
            else
                updata();
        }
    }
}
int main()
{
    while(scanf("%d", &n)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                scanf("%d", &w[i][j]);
        }
        int aa;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                scanf("%d", &aa);
                w[j][i]*=aa;
            }

        }
        km();
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            ans+=lx[i];
            ans+=ly[i];
        }
        printf("%d\n", ans);
    }
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
#define maxn 25
//km算法模板题
int tmp[maxn][maxn], s[maxn][maxn];
int vis[maxn], z[maxn];   //vis=boy,z=girl
int slack[maxn];    //对应的男生至少要降低多少权值
int link[maxn], power_g[maxn], power_b[maxn];
int n, ans;
bool dfs(int x)
{
    int now;    //匈牙利
    z[x]=true;
    for(int i=1; i<=n; i++)
    {
        if(vis[i])
            continue;    //每个男生只能访问一次
        now=power_g[x]+power_b[i]-s[x][i];
        if(!now)   //如果刚好相等就连了
        {
            vis[i]=true;
            if(!link[i]||dfs(link[i]))   //如果对方还没有被匹配or之前那个人可以换走的话
            {  //因为要换走自己这边的人,所以是dfs(link[i])啊
                link[i]=x;
                return true;
            }
        }
        else
            slack[i]=min(slack[i], now);  //不然就获取最小差距,以便调整权值时用
    }
    return false;  //要是前面一直没有被匹配上
}
void KM()
{
    int x;
    for(int i=1; i<=n; i++)
    {
        memset(slack, 0x3f, sizeof(slack));  //因为要获取最小限度,所以初始化为极大值
        while(1)
        {
            memset(vis, 0, sizeof(vis));
            memset(z, 0, sizeof(z));
            if(dfs(i))
                break;
            x=0x3f3f3f3f;
            for(int j=1; j<=n; j++)
            {
                if(!vis[j])
                    x=min(x, slack[j]);   //获取整张图的最小限度
            }

            for(int j=1; j<=n; j++)
            {    //给涉及到的点修改权值
                if(z[j])
                    power_g[j]-=x;  //error!!!不要搞反了,是给女生降低,男生提高
                if(vis[j])
                    power_b[j]+=x;
                else
                    slack[j]-=x;   //因为对面降低了,所以差距也小了
            }
        }
    }
    for(int i=1; i<=n; i++)
        ans+=s[link[i]][i];   //枚举男生,因为link[i]存的是男生对应的女生,所以只有男生才能获取到女生,反之不行
             printf("%d\n", ans);
}
int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            scanf("%d", &tmp[i][j]);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            scanf("%d", &s[i][j]);
            s[i][j]*=tmp[j][i];
            power_g[i]=max(power_g[i], s[i][j]);
        }
    KM();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z6q6k6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值