【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
/////////////////////////////////////////////////


posted @ 2012-04-24 09:09  MXi4oyu  阅读(125)  评论(0编辑  收藏  举报