zoukankan      html  css  js  c++  java
  • 运动员最佳匹配问题 KM算法:带权二分图匹配

    题面:

    羽毛球队有男女运动员各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]。设计一个算法,计算男女运动员最佳配对法,使各组男女双方竞赛优势的总和达到最大。

    题解:

    看完题很容易发现这就是一个带权二分图匹配,

    那么有两种选择:KM算法,费用流,

    由于KM算法时间复杂度更优,这里选择KM算法,

    以P[i][j]*Q[j][i]为边权直接匹配就好了

    我是当KM算法模板做的,,,,

    KM算法网上讲解也蛮多的,这里就不写了(其实是懒得画图)

    这里运动员的数量很少,显然用邻接矩阵更合适,

    匈牙利算法其实也可以看做是权值为1的带权二分图,

    KM算法中也需要用到匈牙利,

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 24
     5 int n,ans;
     6 int tmp[AC][AC],s[AC][AC];
     7 int vis[AC],z[AC];//vis = boy ,z = girl
     8 int slack[AC];//对应的男生至少要降低多少权值
     9 int link[AC],power_g[AC],power_b[AC];
    10 /*km算法模板题*/
    11 
    12 inline void upmax(int &a,int b)
    13 {
    14     if(b > a) a = b;
    15 }
    16 
    17 inline void upmin(int &a,int b)
    18 {
    19     if(b < a) a = b;
    20 }
    21 
    22 void pre()
    23 {
    24     scanf("%d",&n);
    25     for(R i=1;i<=n;i++)
    26         for(R j=1;j<=n;j++)
    27             scanf("%d",&tmp[i][j]);
    28     for(R i=1;i<=n;i++)
    29         for(R j=1;j<=n;j++)
    30         {
    31             scanf("%d",&s[i][j]);
    32             s[i][j] *= tmp[j][i];//求出每条边的权值
    33             upmax(power_g[i],s[i][j]);//初始权值为所有权值里面最大的那个
    34         }
    35 }
    36 
    37 bool dfs(int x)
    38 {
    39     int now;
    40     z[x]=true;
    41     for(R i=1;i<=n;i++)
    42     {
    43         if(vis[i]) continue;//每个男生只能访问一次
    44         now=power_g[x] + power_b[i] - s[x][i];//获取权值和与边权之间的差距(一般会为正?)
    45         if(!now)//如果刚好相等就连了
    46         {
    47             vis[i]=true;
    48             if(!link[i] || dfs(link[i]))//如果对方还没有被匹配or之前那个人可以换走的话
    49             {//因为要换走自己这边的人,所以是dfs(link[i])啊
    50                 link[i]=x;//就连上
    51                 return true;//其实这部分就是匈牙利
    52             }
    53         }
    54         else upmin(slack[i],now);//不然就获取最小差距,以便调整权值时用
    55     }
    56     return false;//要是前面一直没有被匹配上
    57 }
    58 
    59 void KM()
    60 {
    61     int x;
    62     for(R i=1;i<=n;i++)
    63     {
    64         memset(slack,127,sizeof(slack));//因为要获取最小限度,所以初始化为极大值
    65         while(1)//为什么一定要匹配满?貌似是题目要求,,,,
    66         {
    67             memset(vis,0,sizeof(vis));
    68             memset(z,0,sizeof(z));
    69             if(dfs(i)) break;//如果直接就配上了那就算了
    70             x=INT_MAX;
    71             for(R j=1;j<=n;j++)
    72                 if(!vis[j]) upmin(x,slack[j]);//获取整张图的最小限度
    73             for(R j=1;j<=n;j++)
    74             {//给涉及到的点修改权值
    75                 if(z[j]) power_g[j] -= x;//error!!!不要搞反了,是给女生降低,男生提高
    76                 if(vis[j]) power_b[j] += x;
    77                 else slack[j] -= x;//因为对面降低了,所以差距也小了
    78             }
    79         }
    80         
    81     }
    82     for(R i=1;i<=n;i++)
    83         ans+=s[link[i]][i];//枚举男生,因为link[i]存的是男生对应的女生,所以只有男生才能获取到女生,反之不行
    84     printf("%d
    ",ans);
    85 }
    86 
    87 int main()
    88 {
    89 //    freopen("in.in","r",stdin);
    90     pre();
    91     KM();
    92 //    fclose(stdin);
    93     return 0;
    94 }
  • 相关阅读:
    【STM32】串行通信原理
    【STM32】NVIC中断优先级管理
    【Linux-驱动】RTC设备驱动架构
    【Linux-驱动】在sysfs下创建对应的class节点---class_create
    【Linux-驱动】将cdev加入到系统中去---cdev_add
    【Linux-驱动】简单字符设备驱动结构和初始化
    【Linux-驱动】printk的打印级别
    【Linux 网络编程】REUSADDR
    【Linux 网络编程】常用TCP/IP网络编程函数
    linux定时重启tomcat脚本
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9143313.html
Copyright © 2011-2022 走看看