Luogu 1559 运动员最佳匹配问题(带权二分图最大匹配)
2017-07-08 22:10
239 查看
Luogu 1559 运动员最佳匹配问题(带权二分图最大匹配)
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
310 2 3
2 3 4
3 4 5
2 2 2
3 5 3
4 5 1
Sample Output
52Http
Luogu:https://www.luogu.org/problem/show?pid=1559#subSource
带权二分图的最大匹配解决思路
这个题目是一道带权二分图最大匹配的题目,我们要用到的就是KM算法。那么什么是KM算法呢?
通俗点来讲,KM算法就是给匈牙利算法提供一张图,让后者去匹配。
KM算法是基于贪心的思想,选取权值最大的边建图,然后让匈牙利算法去判断其可行性。
我们知道,匈牙利算法的关键就是“让”出边来匹配,那么KM算法的关键就是如何建出这张图。
接下来我们结合代码来看一看。
首先我们对代码中的变量进行一下说明,我们定义二分图的左边这一组为
X,右边一组为Y。定义use_left,use_right分别标记左边或右边的点是否标记,同时标记其是否在增广路中。定义Wx,Wy分别为左边和右边的顶标这是KM算法的关键之处。这个顶标是用来干嘛的呢?这就是用来判断某一条边是否在我们定义的图中的。我们定义只有满足Wx[i]+Wy[j]==W[i][j]的这样一条边i->j才在图中存在。所以初始化的时候,Wx[i]置为从i出发的最大边的权值,而Wy[j]置为0。这就相当于把i->j这条边加入图中。
对于每一个左边的点i,我们首先对其跑一边匈牙利算法,如果发现无法匹配,则进行下面的操作。从剩余的边中选一条出发点i已标记而到达点j未标记(标记操作在匈牙利算法中实现),并满足Wx[i]+Wy[j]-W[i][j]最小。选择这条边,然后把所有已标记的左边的点的Wx加上W[i][j],而右边的已标记的点的Wy分别减去W[i][j]。这相当于把i->j这条边加入到我们要判断的图中,并且原来图中有的边不变。(仔细想一想,结合我们判断某条边是否在图中的方法:Wx[i]+Wy[j]==W[i][j])
推荐一篇关于二分图匹配的KM算法的文章:http://www.cnblogs.com/Lanly/p/6291214.html
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<vector> using namespace std; #define mem(Arr,x) memset(Arr,x,sizeof(Arr)) class EDGE { public: int v,w; }; const int maxN=30; const int inf=2147483647; int n; vector<EDGE> E[maxN];//存二分图的边 int Match[maxN];//存右边匹配的左点编号 bool use_left[maxN];//判断是否访问,一是避免重复,二十判断是否在增广路上,以便后面加边 bool use_right[maxN]; int Wx[maxN];//两边的顶标 int Wy[maxN]; int read(); bool Hungary(int u); int main() { int P[maxN][maxN]; int Q[maxN][maxN]; mem(Match,-1); mem(Wy,0); cin>>n; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) P[i][j]=read(); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) Q[i][j]=read(); for (int i=1;i<=n;i++) { int D=-inf;//初始化左边的顶标 for (int j=1;j<=n;j++) { E[i].push_back((EDGE){j,P[i][j]*Q[j][i]}); D=max(D,P[i][j]*Q[j][i]);//预处理出Wx } Wx[i]=D; Wy[i]=0; } for (int i=1;i<=n;i++) { do { mem(use_left,0); mem(use_right,0); if (Hungary(i)) break; int D=inf; for (int j=1;j<=n;j++) if (use_left[j]==1) { for (int k=0;k<E[j].size();k++) { int v=E[j][k].v; int w=E[j][k].w; if (use_right[v]==0) { D=min(D,Wx[j]+Wy[v]-w); } } } for (int j=1;j<=n;j++) if (use_left[j]) Wx[j]=Wx[j]-D; for (int j=1;j<=n;j++) if (use_right[j]) Wy[j]=Wy[j]+D; } while (1); } int Ans=0; for (int i=1;i<=n;i++) for (int j=0;j<E[Match[i]].size();j++) { if (E[Match[i]][j].v==i) { Ans+=E[Match[i]][j].w; break; } } cout<<Ans<<endl; return 0; } int read()//读入优化 { int x=0; int k=1; char ch=getchar(); while (((ch>'9')||(ch<'0'))&&(ch!='-')) ch=getchar(); if (ch=='-') { k=-1; ch=getchar(); } while ((ch<='9')&&(ch>='0')) { x=x*10+ch-48; ch=getchar(); } return x*k; } bool Hungary(int u)//匈牙利算法判断 { use_left[u]=1; for (int i=0;i<E[u].size();i++) { int v=E[u][i].v; if ((use_right[v]==0)&&(Wx[u]+Wy[v]==E[u][i].w)) { use_right[v]=1; if ((Match[v]==-1) || (Hungary(Match[v])) ) { Match[v]=u; return 1; } } } return 0; }
相关文章推荐
- [Luogu 1559]运动员最佳匹配问题
- Luogu 2756 飞行员配对方案问题(二分图最大匹配)
- 线性规划与网络流24题之分配问题 最大费用最大流、最小费用最大流、二分图的最佳匹配
- hdu5090 匈牙利算法二分图最大匹配问题
- 匈牙利算法—解决二分图最大匹配问题
- HDU 2063 过山车(二分图最大匹配问题)
- 洛谷P1559 运动员最佳匹配问题
- 二分图的最大匹配问题
- 二分图最大匹配问题与匈牙利算法的核心思想
- 利用匈牙利算法&Hopcroft-Karp算法解决二分图中的最大二分匹配问题 例poj 1469 COURSES
- 【网络流二十四题 试题库问题】【二分图多重匹配->最大流】
- 【网络流24题】No.7 试题库问题 (最大流,二分图多重匹配)
- poj 3692(浅谈二分图最大匹配求最大独立集在解决最大团问题中的应用)
- 二分图的最大匹配和最小点覆盖问题
- 运动员最佳匹配问题
- 骑士共存问题 (二分图最大匹配 转换 网络最大流 )
- 飞行员配对方案问题(二分图的最大匹配)
- [SMOJ2205]飞行员配对方案问题(二分图最大匹配)
- [luoguP2764] 最小路径覆盖问题(最大流 || 二分图最大匹配)
- 关于二分图的最大匹配(最小点覆盖)问题的资料整理