【100题】第三十六 比赛淘汰问题(谷歌笔试)
一,题目:(谷歌笔试)
n支队伍比赛,分别编号为0,1,2……n-1,已知它们之间的实力对比关系,存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中,比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。.......胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排,下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名
编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],求出result(特别注意:result输出的是从第一名到最后一名)
二,分析
这里有一个简单的做法,就是:查看每一行重复数字个数,如果重复数字个数为n则排名为1,重复数字个数n-1则排名为2。所以重复数字个数为i个,则排名为n+1-i;给出order[],则相应的result很容易得出。这里值得注意的是:order仅仅为出场顺序,跟result没有任何关系。
对应源码为:
#include <iostream> #include <stack> using namespace std; /*order 顺序为pk顺序*/ int main() { int w[6][6] = { //排名 重复数 0,1,2,3,0,0, //4 3 1,1,2,1,1,1, //2 5 2,2,2,2,2,2, //1 6 3,1,2,3,3,3, //3 4 0,1,2,3,4,5, //6 1 0,1,2,3,5,5 //5 2 }; int order[6] = {1,3,4,2,0,5}; int weight[6];//2,3,6,1,4,5 int result[6]; int max; for(int i=0;i<6;i++) { max=0; int temp[6]={0,0,0,0,0,0}; for(int j=0;j<6;j++) { temp[w[i][j]]++; } for(int k=0;k<6;k++)//获取排名 { if(max<temp[k]) max=temp[k];//获取最大重复数 } weight[i]=7-max;//排名 } int n=6; int count=6; int count_left=0; int flag; while(count) { count_left=0; for(int i=0;i<6;i=i+2) { if(weight[order[i]]>weight[order[i+1]]) { result[--count]=order[i];//排名在后面的直接 order[count_left++]=order[i+1];//排名在前面的 } else { result[--count]=order[i+1]; order[count_left++]=order[i]; } if(i!=0) { order[count_left++]=order[i+1]; } if(count==0) result[0]=order[0]; } // cout<<"count "<<count<<endl; } //result[0]=weight[order[i]]; for(int i=0;i<6;i++) { cout<<result[i]<<endl; } return 0; }
上述源码中,明显的缺点就是,通过记录重复个数来判断哪个优先级高。这里完全可以根据两个元素行与列交叉处值来判断。
改进后源码:
#include <iostream> #include <stack> using namespace std; /*order 顺序为pk顺序*/ int main() { int w[6][6] = { //排名 重复数 0,1,2,3,0,0, //4 3 1,1,2,1,1,1, //2 5 2,2,2,2,2,2, //1 6 3,1,2,3,3,3, //3 4 0,1,2,3,4,5, //6 1 0,1,2,3,5,5 //5 2 }; int order[6] = {1,3,4,2,0,5}; int result[6]; int count=6; int count_left=0; int flag; while(count) { count_left=0; for(int i=0;i<6;i=i+2) { int nWiner = *((int*)w + 6 * order[i] + order[i+1]);//取两个数交叉的数 if(nWiner==order[i+1]) { result[--count]=order[i];//排名在后面的直接 order[count_left++]=order[i+1];//排名在前面的 } else{ result[--count]=order[i+1]; order[count_left++]=order[i]; } if(i!=0) { order[count_left++]=order[i+1]; } if(count==0) result[0]=order[0]; } } for(int i=0;i<6;i++) { cout<<result[i]<<endl; } return 0; }
三,源码(利用模板list)
#include <stdio.h> #include <list> #include <iostream> using namespace std; void raceResult(int** w, int* order, int* result, int n) { list<int> winer; int count = n; while(n) { winer.push_front(order[--n]); } int resultNum = count - 1; int nFirst, nSecond; int round = 1; while(winer.size() > 1) { //一轮开始 cout<<endl<<"round "<<round++<<endl; list<int>::iterator it = winer.begin(); while (it != winer.end()) { nFirst = *it; if (++it == winer.end()) { //轮空 cout<<nFirst<<" rest this round"<<endl; } else { nSecond = *it; //已经向后移动 int nWiner = *((int*)w + count * nFirst + nSecond);//取两个数交叉的数 if (nWiner == nFirst) { it = winer.erase(it); result[resultNum--] = nSecond; cout<<nFirst<<" kick out "<<nSecond<<endl; } else { it = winer.erase(--it); result[resultNum--] = nFirst; ++it; cout<<nSecond<<" kick out "<<nFirst<<endl; } } } } if (winer.size() == 1) { result[0] = winer.front(); } cout<<endl<<"final result: "; int nPlace = 0; while(nPlace < count) { :cout<<endl<<result[nPlace++]; } } void test() { //team 2>team 1>team 3>team 0>team 4>team 5 int w[6][6] = { 0,1,2,3,0,0, 1,1,2,1,1,1, 2,2,2,2,2,2, 3,1,2,3,3,3, 0,1,2,3,4,5 }; int order[6] = {1,3,4,2,0,5}; int result[6] = {-1}; raceResult((int**)w, order, result, 6); getchar(); } //自己加上主函数,测试了下,结果竟正确.. int main() { test(); return 0; }
///////////////////////////////////////////////
round 1
1 kick out 3
2 kick out 4
0 kick out 5
round 2
2 kick out 1
0 rest this round
round 3
2 kick out 0
final result:
2
0
1
5
4
3
/////////////////////////////////////////////////