64匹马,8个赛道,找出跑得最快的4匹马,至少比赛几场?
下面是我对这道题的理解,借用了64匹马,8个赛道问题的帖子。
相信这个问题不少同学在面试过程中遇到过。这题是道智力题,我们应该通过画图的方式来实现。我们知道油64匹马,8个跑道,那么根据贪心思想,我们应该让所有马都上跑道,这样跑8次,可以淘汰一半的马。如下图所示:
我们得到每组的前四名,共计8 * 4 = 32匹,接下来我们应该怎么做呢,4匹4匹的荷载一起,四个跑道比4次吗?答案是否定的,这样等于没有利用每个赛道的4匹之间的先后属性,我们应该使用每个赛道的头名进行比赛,这样只需要比一次就可以淘汰一半的马。这时候总冠军已经诞生,他就是头名比赛中还是头名的那一匹。
接下来我们得从剩下得15匹中选择3匹,我们在比赛之前,先分析一下现在得情况,对于剩下得15匹,第一名所在得那一条赛道剩下的马是不是都有可能,第二名所在的赛道的最后一匹是不可能入选的,第三名所在的赛道的最后两匹也是不可能入选的,以此类推,最后我们只需要比较剩下的9匹即可。我们通过随机选择8匹,然后获得前3匹,与剩下的一匹组合4匹再跑一次,选出剩下3匹。
综上所述,总共需要11次比赛就能得出前4匹最快的马。
以下是代码模拟过程,我们先建立一个horse类,包含两个属性,编号和速度。通过随机生成速度的方式,生成64匹速度不同的马作为测试用例,然后经过上述算法模拟输出,与直接排序法进行比较,验证结论。
我们将赛道和马都变量化,这样可以测试16个赛道,4匹马等多种情况。
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
/**
* 64匹马,8个赛道,找出跑得最快的4匹马,至少比赛几场?
* 每个赛道最多容纳8匹马同时跑
* @author brooke
*
*/
public class fastHorses {
Horse[] horses;
public fastHorses(int n, int speed) {
this.horses = generateHorse(n, speed);
}
/**
* 生成n匹马,speed表示马的速度范围(不是实际的马速度)
* @param n
* @param speed
* @return
*/
public Horse[] generateHorse(int n, int speed) {
Horse[] horses = new Horse[n];
List<Horse> list = new ArrayList<>();
for(int i=0;i<n;i++) {
horses[i] = new Horse(i + 1, (int)(Math.random() * speed) + 1);
list.add(horses[i]);
}
list.sort((o1, o2)->{return o2.speed - o1.speed;});
for(int i=0;i<list.size();i++) {
System.out.print(list.get(i));
}
System.out.println();
return horses;
}
/**
* 获取最终的target匹最快的马需要的最少比较次数
* @param capacity 是赛道容量
* @param target
* @return
*/
public int getMinCompares(int capacity, int target) {
if(capacity < target) {
System.out.println("Input invalid!");
return -1;
}
if(horses.length % capacity != 0) {
System.out.println("暂不支持!");
return -2;
}
List<List<Horse>> lists = new ArrayList<>();
List<Horse> list = new ArrayList<>();
int num = 0;
int cnt = 0;
//第一次,每8匹一组,选出前4匹,共比赛8次
for(int i=0;i<horses.length;i++) {
if(cnt++ < capacity) {
list.add(horses[i]);
} else {
num++;
lists.add(HorseCompitition(list, target));
list = new ArrayList<>();
cnt = 0;
i--;
}
}
num++;
lists.add(HorseCompitition(list, target));
list = new ArrayList<>();
cnt = 0;
//第二次,每组第一名开始比较,选出前4匹
List<Horse> temp = null;
for(int i=0;i<lists.size();i++) {
list.add(lists.get(i).get(0));
}
num++;
temp = HorseCompitition(list, target);
list = new ArrayList<>();
List<Horse> res = new ArrayList<>();
res.add(temp.get(0));//找到最快的一匹
for(int i=0;i<lists.size();i++) {
Horse horse = lists.get(i).get(0);
if(!temp.contains(horse)) {
lists.remove(i--);
}
}
//16匹要淘汰6匹(推导),然后最快的一匹已经选出,剩余9匹,采用随机选择8匹参与比赛
//然后取前3匹与剩下的一匹比赛,选出最后的3匹,加上最快的一匹就是答案
for(List<Horse> horses : lists) {
int num1 = target - temp.indexOf(horses.get(0));
for(int i=0;i<num1;i++) {
list.add(horses.get(i));
}
}
list.remove(res.get(0));
int tempindex = (int)(Math.random() * list.size());
Horse temphorse = list.get(tempindex);
list.remove(tempindex);
num++;
list = HorseCompitition(list, target - 1);
list.add(temphorse);
num++;
list = HorseCompitition(list, target - 1);
res.addAll(list);
for(Horse horse : res)
System.out.print(horse);
return num;
}
/**
* 从list中选出前target个返回(模拟比赛)
* @param list
* @param target
* @return
*/
private List<Horse> HorseCompitition(List<Horse> list, int target){
if(list.size() < target) {
return list;
}
List<Horse> res = new ArrayList<>();
if(target <= 0) {
return res;
}
PriorityQueue<Horse> queue = new PriorityQueue<>((o1, o2)->{return o1.speed - o2.speed;});
for(int i=0;i<list.size();i++) {
if(queue.size() < target) {
queue.add(list.get(i));
}else {
if(list.get(i).speed > queue.peek().speed) {
queue.poll();
queue.add(list.get(i));
}
}
}
while(!queue.isEmpty()) {
res.add(0, queue.poll());
}
return res;
}
public static void main(String[] args) {
fastHorses test = new fastHorses(64, 100);
System.out.println("\nthe min compares is " + test.getMinCompares(8, 4));
}
}
class Horse{
int id; // 马的编号
int speed; // 马的速度值
public Horse(int id, int speed) {
super();
this.id = id;
this.speed = speed;
}
@Override
public String toString() {
return "Horse [id=" + id + ", speed=" + speed + "]";
}
}
测试结果: