运动员最佳匹配问题 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 }