题解——运动员最佳匹配问题 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\n",ans);
85 }
86 
87 int main()
88 {
89 //    freopen("in.in","r",stdin);
90     pre();
91     KM();
92 //    fclose(stdin);
93     return 0;
94 }

猜你喜欢

转载自www.cnblogs.com/ww3113306/p/9143313.html
今日推荐