一、 羽毛球队有男女运动员各 n n n人。给定2 个 n × n n×n n×n 矩阵 P P P 和 Q Q Q。 P [ i ] [ j ] P[i][j] P[i][j] 是男运动员 i i i 和女运动员 j j j 配对组成混合双打的男运动员竞赛优势; Q [ i ] [ j ] Q[i][j] Q[i][j] 是女运动员 i i i 和男运动员 j j j 配合的女运动员竞赛优势。由于技术配合和心理状态等各种因素影响, P [ i ] [ j ] P[i][j] P[i][j]不一定等于 Q [ j ] [ i ] Q[j][i] Q[j][i]。男运动员 i i i 和女运动员 j j j 配对组成混合双打的男女双方竞赛优势为 P [ i ] [ j ] × Q [ j ] [ i ] P[i][j]\times Q[j][i] P[i][j]×Q[j][i]。设计一个算法,计算男女运动员最佳配对法,使各组男女双方竞赛优势的总和达到最大。
-
选用回溯法进行求解。
首先将 n n n 个男运动员和 n n n 个女运动员分别从小到大编号为 1 ∼ n 1\sim n 1∼n ,之后将 n n n 个男运动员的位置确定,对 n n n 个女运动员进行全排列,可以用构建排列树的方法求解。求解结果应为:
max ∑ i = 1 n ∑ j = 1 n P [ i ] [ j ] × Q [ j ] [ i ] \max \sum_{i=1}^{n} \sum_{j=1}^{n} P[i][j]\times Q[j][i] maxi=1∑nj=1∑nP[i][j]×Q[j][i]
解空间: 以 n = 4 n=4 n=4 为例,构建出的排列树的解空间如下图所示(仅给出了排列中第一个女生为编号1时的部分,边上的值表示为排列的女生编号):
可行性约束条件: 无
限界函数: 设置宽松限界条件:若前 k k k 个小组与剩余的任意组合的最大竞争优势相加之和 比 当前已找到的最大竞争优势还小,那该方案必不满足,可以进行剪枝。
对于
n
n
n 个男运动员,先求得其与女运动员排列的最大值,即求
P
P
P 矩阵每一行中的最大值进行相加,得到
r
r
r ,每次减去当前的最大值。即:
r
=
∑
i
=
k
+
1
n
max
1
≤
m
≤
n
(
P
[
i
]
[
m
]
×
Q
[
m
]
[
i
]
)
r=\sum_{i=k+1}^n \max_{1\leq m\leq n} (P[i][m]\times Q[m][i])
r=i=k+1∑n1≤m≤nmax(P[i][m]×Q[m][i])
故该限界函数为:
∑
i
=
1
k
P
[
i
]
[
j
]
×
Q
[
j
]
[
i
]
+
∑
i
=
k
+
1
n
max
1
≤
m
≤
n
(
P
[
i
]
[
m
]
×
Q
[
m
]
[
i
]
)
>
m
a
x
f
\sum_{i=1}^k P[i][j]\times Q[j][i] +\sum_{i=k+1}^n \max_{1\leq m\leq n} (P[i][m]\times Q[m][i])> maxf
i=1∑kP[i][j]×Q[j][i]+i=k+1∑n1≤m≤nmax(P[i][m]×Q[m][i])>maxf
构建好树之后,求解过程的算法描述如下:
public class Athe{
static int n; //男女运动员配对组数
static int f; //当前计算下竞赛优势的总和
static int r; //当前剩余组合的最大竞争优势
static int maxf; //当前最大值
static int [][]p; //男运动员竞赛优势
static int [][]q; //女运动员竞赛优势
static int []max_line; //二元数组p中每一行中的最大值
static int []x; //当前的排列顺序
static int []maxx; //当前最优的排列顺序
private static void maxLine(int x) //求每一行中的最大值
{
int thismax=0;
for (int i=0; i<n; i++){
for (int j=0; j<n; j++){
if(thismax < p[i][j])
thismax=p[i][j]
}
max_line[i]=thismax;
}
}
public static void cal_r() //计算初始r值
{
for (int i=0; i<n; i++){
r += max_line[i];
}
}
private static void backtrack(int i)
{
if(i>n){ //到达叶子结点,搜索到最底部
for(int j=1; j<=n; j++) maxx[j]=x[j];
maxf=f;
}
else{ //非叶子结点
for(int j=i; j<=n; j++){
f += P[i][x[j]]*Q[x[j]][i];
r -= max_line[i];
if(f+r > maxf){ //限界条件
MyMath.swap(x,i,j);
backtrack(i+1);
MyMath.swap(x,i,j);
}
f -= P[i][x[j]]*Q[x[j]][i];
r += max_line[i];
}
}
}
}