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