问题描述:假设有16个球,有david和cavin两个人轮流来取,每个人只能去1,3,6.先取完的为胜。由David先取,问David第一次去多少才能保证胜利。
首先要感谢湛总,这个思路是它提供的。
首先建立两个建立,一个是必胜集,一个是必输集。而且只考虑一个人,比如David,如果当前球的数量正好在必胜集中,那么他一定可以取胜,假如自己在必输集里,假设Calvin智力正常,那David一定输了。
首先他们之间的转换关系如下:如果一个数,该数-1,-3,-6只要有一个在必输集里面,那么这个点就加入必胜集中。原因很简单,David取了一个数之后,将某个必输的结果留给了Calvin,那这个数对David而言自然就稳赢了,因为David不是傻子,他取的数一定对它是最有利的。同理,如果一个数,该数-1,-3,-6之后这些数全在必胜集里面,那他必输。
所以经过一个循环之和,这两个集合也就建立起来了,既然David先取,它只要选择一个数,使余下的数在必输集里面就OK了,因为这个必输集是Calvin需要面对的。
代码如下:
package 腾讯模拟考.problem1;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
* Created by dave on 2016/9/1.
* 假设有16个球,有david和cavin两个人轮流来取,每个人只能去1,3,6.先取完的为胜。由David先取,问David第一次去多少才能保证胜利
*/
public class Main {
static int[] steps = new int[]{1,3,6};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int count = scanner.nextInt();
int choose = doWork(count);
System.out.println(choose);
}
private static int doWork(int count){
Set<Integer> allowSet = new HashSet<>();
Set<Integer> failSet = new HashSet<>();
for(int tmp:steps)
allowSet.add(tmp);
for(int i = 1;i<count;i++){
if(allowSet.contains(i))
continue;
if(checkIsAllowed(failSet,i))
allowSet.add(i);
else if(checkIsFailed(allowSet,i))
failSet.add(i);
}
for(int tmp:steps){
if(failSet.contains(count-tmp))
return tmp;
}
return -1;
}
private static boolean checkIsAllowed(Set<Integer> set,int val){
for(int tmp:steps){
tmp = val-tmp;
if(tmp > 0 && set.contains(tmp))
return true;
}
return false;
}
private static boolean checkIsFailed(Set<Integer> set,int val){
for(int tmp:steps){
tmp = val-tmp;
if(tmp > 0 && !set.contains(tmp))
return false;
}
return true;
}
}