历届试题 高僧斗法

题目描述

对应题目链接: http://lx.lanqiao.cn/problem.page?gpid=T37

问题描述

  古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
  节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图1所示)
  两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
  两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
  对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。

输入格式

  输入数据为一行用空格分开的N个整数,表示小和尚的位置。台阶序号从1算起,所以最后一个小和尚的位置即是台阶的总数。(N<100, 台阶总数<1000)

输出格式

  输出为一行用空格分开的两个整数: A B, 表示把A位置的小和尚移动到B位置。若有多个解,输出A值较小的解,若无解则输出-1。

样例输入

1 5 9

样例输出

1 4

样例输入

1 5 8 10

样例输出

1 3

测评结果:

 171731_GBef_2851839.png

算法思路

   一开始看到这道题基本是想用递归来求解,如果己方下一步对方必输则我方就赢了,如果没有办法让对方输己方就输了。但是这个复杂度有点高,假设小和尚n个台阶m个那么我们要选一个小和尚走大约是O(n*m)但是它又会产生O(n*m)种新的布局我们递归就是将这些布局继续算下去,因此是个指数级别的增长。靠递归完成这道题基本不可能。

   由于这道题是无偏差游戏,就是前后两个法师身份没有区别。也就是说他们只有前后的区别,小和尚没有归属于任何一方这就是身份无差别,如果游戏中某个角色归属于某一方这就不是无偏差游戏,比如象棋,它的棋子是分了红黑两边的就不是无偏差游戏。

    无偏差游戏可以利用尼姆定理去解决,说道尼姆定理你要看下尼姆堆这个游戏是啥。尼姆堆游戏就是给几堆东西然后让两个人选,一次可以从任意一堆选任意数量的物品。直到把所有的东西取完,当一个人没法取的时候他就是输了。

  而尼姆定理用计算机描述语言就是说当将尼姆堆的数字变成二进制时候如果任何一列的1的个数变成偶数那么对方就输了。

  我们就是要将高僧斗法这个游戏转换成类似尼姆堆这种游戏,有几堆东西,然后任意取,然后直到取完就输了。

  这样分析就可以发现如果我们把两个和尚之间的台阶数作为一堆东西,刚好满足上面的条件,高僧任意将两个小和尚之间距离变短,直到没法移动(取完了)就输了。注意一点的是:取任何一个堆的东西不能影响其他堆。因为尼姆堆就是一次只从一个堆里面取东西。

 我程序思路就是遍历一遍第一个法师第一次可以走的路,然后每走一次检查一下尼姆堆列方向1的个数是否是偶数如果所有列都是偶数则第一个法师必赢输出结果。下面是如何检查尼姆堆列方向1的个数是否是偶数的分析。如果不清楚建议可以看下代码再来这些分析:

从题目给的输入案例分析:

1 5 9

那么我们把1-5之间距离作为一个尼姆堆,他们之间台阶数是3,转换成二进制数是101.

我们遍历第一个法师可以走的方法,找到1移动到4可以使得尼姆堆变成0使得列方向的1个数为偶数下一个法师就必输.

再看一个案例:

1 5 8 10

我们将1-5  、 8-10组合成两个尼姆堆,台阶数分别是3,1。转换成二进制

101

    1

然后我们用程序遍历一次走法找到从1到3的时候尼姆堆变成了下面这个情况:

1

1

因此列就变成了偶数下一个法师必输。

 具体代码


import java.util.Scanner;

public class DouFa {
	public static int[] a;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		String data = input.nextLine();
		input.close();
		String[] digitstr = data.split(" ");
		a = new int[digitstr.length];
		for (int i = 0; i < digitstr.length; i++)
			a[i] = Integer.parseInt(digitstr[i]);
		for (int i = 0; i < a.length - 1; i++) {//依次选一个和尚
			int origin = a[i];
			for (int j = a[i] + 1; j < a[i + 1]; j++) {
          //遍历选中和尚可以走的路
				a[i] = j;
				if (f()) {
       // 每走一次判断是否下一个法师输对方输己方赢,如果能赢直接输出结果
					System.out.println(origin + " " + a[i]);
					return;
				}
			}
			a[i] = origin;
		}
		System.out.println(-1);

	}

	// 检查当前情形是否能赢
	private static boolean f() {
		int[] onenum = new int[32];
		int maxlen = 0;
//统计分成的尼姆堆各列1的个数
		for (int i = 0; i < a.length-1; i += 2) {
			String tempbinary = Integer.toBinaryString(a[i + 1] - a[i] - 1);
//将两个和尚之间的台阶数转成二进制
			int len = tempbinary.length();
			if (len > maxlen)
				maxlen = len;
			for (int j = len - 1; j >= 0; j--)//
				if (tempbinary.charAt(j) == '1')
					onenum[len - j - 1]++;
		}
		for (int i = 0; i < maxlen; i++)
			if (onenum[i] % 2 != 0)
//如果有一列不满足1是偶数那么下一个法师肯定不会输,己方就会输
				return false;
		return true;//如果所有列1个数都是偶数己方必赢
	}
}

 

转载于:https://my.oschina.net/yuanmuou/blog/898907

你可能感兴趣的:(历届试题 高僧斗法)