实验内容: 一、算法实现题: 1、问题描述: 羽毛球队有男女运动员各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]。设计一个算法,计算男女运动员的最佳配对法,使各组男女双方竞赛优势的总和达到最大。
2、编程任务: 设计一个算法,对于给定的男女运动员竞赛优势,计算男女运动员最佳配对法,使各组男女双方竞赛优势的总和达到最大。
3、数据输入: 由文件input.txt给出输入数据;第一行有1个正整数n(1≤n≤20);接下来的2n行,每行n个数,前n行是P,后n行是Q。
4、结果输出: 将计算的男女双方竞赛优势的总和的最大值输出到文件output.txt。 输入文件示例 输出文件示例 intput.txt output.txt 3 52 10 2 3 2 3 4 3 4 5 2 2 2 3 5 3 4 5 1 二、解题思路 1、求问题的解空间 对于n个男运动员,从第1个开始搭配女运动员:第1个有n种搭配方法,第2个有n-1种搭配方法……第n个有n-(n-1)种搭配方法;根据问题给出的示例:输入n的值为3,表示男女运动员各有3名; 男运动员 1 2 3按顺序搭配女运动员,他们分别对应的女运动员可以是: 女运动员 1 2 3、1 3 2、2 1 3、2 3 1、3 1 2、3 2 1 所以其解空间是{(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)},整个问题可看成是1,2,3的全排列问题,将解空间组织成一棵排列树如下
解空间树的第i层到第i+1层边上的标号给出了变量的值。从树根到叶的任一路径表示解空间中的一个元素。 2、用深度优先的搜索策略搜索整个解空间 用一个数组x记录可行解,并且赋初值为{0,1,2…n},x[i]表示第i个男生配对的女生号;所以第i个男生与第x[i]个女生双方竞赛优势为P[i-1][x[i]-1]*Q[x[i]-1][i-1]; 用t表示递归深度,初值为1,表示第1个男生开始搭配;t可以由n来控制。 当t>n时,表示算法已搜索到叶子结点,此时根据x的记录计算男女双方竞赛优势的总和s,算法如下: P[j][x[j+1]-1]*Q[x[j+1]-1][j]表示第j+1个男运动员与第x[j+1]个女运动员配对组成混合双打的男女双方竞赛优势 int s=0; //记录当前男女双方竞赛优势总和,计算前值为0 for(int j=0;j<n;j++) s=s+P[j][x[j+1]-1]*Q[x[j+1]-1][j]; 并判断s是否大于当前记录的男女双方竞赛优势的总和的最大值sum,若s>sum,则给sum赋值s; 当t<=n时,表示还没有搜索到叶子结点,则需要继续往下搜索,从i=t开始搜索至i=n; for(int i=t;i<=n;i++){ Swap(&x[t],&x[i]); //交换x[t]和x[i]的值,第t和第i个男运动员交换搭档 //没有约束函数和限界函数 Backtrack(t+1); //进行第t+1个男运动员配对 Swap(&x[t],&x[i]); //交换回来它们的值,以便于与另外的另外的男运动员交换 } 三、算法描述 #include "fstream.h"
ifstream fin("input.txt"); ofstream fout("output.txt");
int **P=NULL; int **Q=NULL; int *x=NULL; int sum=0; //记录男女双方竞赛优势的总和的最大值 int n; //交换两个数 void Swap(int *b,int *y) { int z; z=*b; *b=*y; *y=z; } //求男女双方竞赛优势总和最大值函数 void Sum() { int s=0; //记录当前男女双方竞赛优势总和 for(int j=0;j<n;j++) s+=P[j][x[j+1]-1]*Q[x[j+1]-1][j]; if(s>=sum) sum=s; //输出值 } //回溯法搜索排列树 void Backtrack(int t) { if(t>n) Sum(); //当到叶子节点时 else for(int i=t;i<=n;i++) { //没有约束函数和限界函数的全排列问题 Swap(&x[t],&x[i]); Backtrack(t+1); //进行第t+1个男运动员配对 Swap(&x[t],&x[i]); } } void main() { int i,j; int t=1; fin>>n; if(n<1||n>20) { fout<<"请输入正确的n的值!"; return; } //动态分配数组空间 x=new int[n+1]; for(i=0;i<n+1;i++) x[i]=i; //P,Q分配n行n列:从第0行0列到第n-1行第n-1列 P=new int*[n]; for(i=0;i<n;i++) P[i]=new int[n]; Q=new int*[n]; for(i=0;i<n;i++) Q[i]=new int[n];
for(i=0;i<n;i++) for(j=0;j<n;j++) fin>>P[i][j]; for(i=0;i<n;i++) for(j=0;j<n;j++) fin>>Q[i][j]; Backtrack(t); fout<<sum; //输出男女双方竞赛优势的总和的最大值 //释放空间 for(int k=0;k<n;k++) { delete[] P[k]; P[k]=NULL; } for(k=0;k<n;k++) { delete[] Q[k]; Q[k]=NULL; } fin.close(); fout.close(); return; } | |||||||||||||||||||||||||||||||||||
数据记录和计算:
| |||||||||||||||||||||||||||||||||||
结论(结果): 这次实验是用回溯法来解决运动员最佳配对问题,程序成功通过编译,在起初调试的时候总是出现应用程序错误,“"0x00401057"指令引用的"0x00000000"内存不能为"written"。要终止程序,请单击"确定"。”后来经过单目调试,一步步解决了内存问题,至能正常运行,并得到正确的输出结果。 | |||||||||||||||||||||||||||||||||||
小结: 通过这次实验,基本上掌握了用回溯法解题的算法框架,首先要理解问题,找出问题的解空间;其次是根据解空间画出解空间树;最后按照深度优先的搜索策略来搜索整棵树来得到最优解。 实验让我更进一步地理解了回溯法的深度优先搜索策略,从根结点出发,根结点成为活结点,成为当前的扩展结点,搜索向纵深方向移至一个新结点,这个新结点会成为新的活结点,在当前扩展结点处不能再向纵深方向移动,当前扩展结点就成为死结点;此时往回移动至最近的一个活结点,就这样递归地在解空间中搜索,直到找到所要求的解或解空间中已无活结点为止。
|
自己以前的作业,做一下收藏而已