Nim问题 不想出差的HR

[编程题]不想出差的HR

按照卡中心校园招聘的要求,HR小招和小商需要从三个科室中(分别为A、B、C)抽派面试官去往不同城市。
两名HR按照以下规定轮流从任一科室选择面试官:每次至少选择一位,至多选择该科室剩余面试官数。最先选不到面试官的HR需要自己出差。
假设HR小招和小商都不想出差且每次选择都采取最优策略,如果是小招先选,写一个函数来判断她是否需要出差。如果不需要出差,请给出第一步的最优策略。

输入描述:
输入为三个正整数,分别代表三个科室的面试官人数,用英文逗号分隔

输出描述:
若小招需要出差,则输出:1;
若小招不需要出差,则输出:第一步选择的科室名称和选择人数,用英文逗号分隔

输入例子1:
1,8,9

输出例子1:
1

输入例子2:
2,0,4

输出例子2:
C,2

分析:
这是Nim问题:
经典的Nim问题是:有若干堆石子,两个人轮流去拿某一堆石子中的一个或者多个,直到最后所有石子都被拿完,最后一个不能拿到石子的人失败。

定义两种情况
1当前先手必败
2当前先手必胜

以两堆石子为例,(3,3)的堆为例,(3,3)的子堆为(3,1),(3,2),(3,0)【石子之间的顺序不影响】,
1.(3,0)的所有可能为【(0,0)必败, (0,1)必胜, (0,2) 必胜 】;所以(3,0)肯定就先手必胜。

2. (3,1)的所有可能【(3,0)必胜,(2,1),(0,1)必胜,(1,1)】;
我们继续看(2,1)和(1,1),只要证明(2,1)or(1,1)是必败,(3,1)就一定是必胜。只要下一回合有一种可能先手必败,那么本回合一定是先手必胜,因为每个人都是选择最优策略让对手失败。
(2,1)可能的结果【(2,0)必胜,(1,1),(1,0)必胜】,继续看(1,1)所有可能的结果为【(1,0)】必胜,因为(1,1)下一回合所有结果都是先手必胜,那这一回合一定是先手必败,(0,1)先手必胜,那么(1,1)一定是先手必败。我们得出(2,1)下回合所有可能都是【(2,0)必胜,(1,1)必败,(1,0)必胜】,那么(2,1)必胜。再往前看(3,1)下回合所有可能【(3,0)必胜,(2,1)必胜,(0,1)必胜,(1,1)必败】,那么(3,1)必胜

3.(3,2)的所有可能【(3,1)必胜,(3,0)必胜,(2,2),(2,1)必胜】,(2,2)的所有可能【(2,1)必胜->【(2,0)必胜,(1,1)必败,(1,0)必胜】,(2,0)必胜】,(2,2)必败
所以(3,2)必胜

4.(3,3)的所有可能【(3,2)必胜,(3,1)必胜,(3,0)必胜】,(3,3)必败

我们可以通过推理本回合接下来的每个回合的输赢,来回溯判断本回合是必胜还是必输。

以上的性质可以通过a1 xor a2 xor a3 … xor ai 来表示。非常神奇

要证明结论是否正确,证明三个命题:
1.将所有不能移动到必胜局面的结果判断为必败局面
2.必胜局面一定可以移到必败局面
3.必败局面的所有下一个局面都是必胜局面

命题1:所有必败局面,a1 xor a2 xor a3 … xor ai xor an == 0

命题2:对于某个局面必胜 a1 xor a2 xor a3 … xor ai xor an!== 0,一定有个合法移动可以移动,使
a1 xor a2 xor a3 … xor ai =0,不妨设a1 xor a2 xor a3 … xor ai xor an == k,一定存在某个ai,他二进制最高位的1和k二进制最高位的1相同,ai xor k =ai’ , ai’小于ai,就好像从石子顿上拿掉了石子,使a1 xor a2 xor …ai’ xor an == 0必败一定成立

命题3:对于某个局面 a1 xor a2 xor a3 … xor ai==0,不存在一个合法移动,将ai变成ai’ 后,使
a1 xor a2 xor a3 … xor ai’ xor an == a1 xor a2 xor a3 … xor ai xor an , 因为ai=ai’,所以ai’不是一个合法的移动

public class Main {
	public static int getHigh(int a) {
		int c=0;
		while(a!=0) {
			a=a>>1;
			c++;
		}
		return c;
		
	}
	
	public static void main(String args[]){
	
		

		Scanner scanner=new Scanner(System.in);
		String string=scanner.nextLine();
		String s[]=string.split(",");
		int c[]=new int[s.length];
		for(int i=0;i<c.length;i++) {
			c[i]=Integer.parseInt(s[i]);
		}
		int p=0;
		for(int i=0;i<c.length;i++) { 
			p=p^c[i];  
		}
		if(p==0) {System.out.println(1); return;} //p为0必败
		else {
			int i=0;
			for( i=0;i<c.length;i++) {
				if(getHigh(p)==getHigh(c[i])) {  //拿到合法移动
					p=p^c[i];  
					break;
				}
				
			}
			int res=0;
			
		    res=c[i]-p;//计算需要拿走的数量
			
			System.out.println((char)('A'+i)+","+res);
		}
		
		

		
		
	}
	

	
 
	        
	    
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值