一个算法题目求助

幸福村1 2014-04-21 02:27:40
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,

* 每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;
* 二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。
* 现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

输入参数:
包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000;
输出参数:包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0

我的思路是最后一我只要走出(1,2)或(2,1)留给对方,不管他怎么拿,我必赢;但我又必须避免拿出(4,3),(6,5)...(2+n,1+n)..(2+2n,1+2n)...,对手也是最优策略想把留给我(2,1),我没有思路了继续了,求助大家这个算法该怎么走,我的思路能继续吗,或者重新找新更巧妙的办法,求助求助。
...全文
649 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
yyfhz 2014-05-05
  • 打赏
  • 举报
回复
至少可以证明,对于石头堆(a,b),的情况下,任意取a,必定存在一个b,b<=2a,使得石头堆(a,b)是先拿者必输。 理由如9L所示。 1. 任意一行中,最多只存在1种先拿者输掉的方法,设为(a0,b0) 2. 这种先拿者必输的方案,在另一行a1上只可能有2个先拿者胜的投影(a1, b0), (a0+(a1-a0), b0+(a1-a0)) 3. 所以对于行a1而言,从行1到行a1-1最多只能有2*a1个先拿者胜的投影。 4.那么接下来的那一个元素自然是先拿者必输的。 另外算了一下,在200*200的范围内,先拿者负的情况为(这里去掉了重复的情况,将数量较少的石堆放在前面) 1 , 2 3 , 5 4 , 7 6 , 10 8 , 13 9 , 15 11 , 18 12 , 20 14 , 23 16 , 26 17 , 28 19 , 31 21 , 34 22 , 36 24 , 39 25 , 41 27 , 44 29 , 47 30 , 49 32 , 52 33 , 54 35 , 57 37 , 60 38 , 62 40 , 65 42 , 68 43 , 70 45 , 73 46 , 75 48 , 78 50 , 81 51 , 83 53 , 86 55 , 89 56 , 91 58 , 94 59 , 96 61 , 99 63 , 102 64 , 104 66 , 107 67 , 109 69 , 112 71 , 115 72 , 117 74 , 120 76 , 123 77 , 125 79 , 128 80 , 130 82 , 133 84 , 136 85 , 138 87 , 141 88 , 143 90 , 146 92 , 149 93 , 151 95 , 154 97 , 157 98 , 159 100 , 162 101 , 164 103 , 167 105 , 170 106 , 172 108 , 175 110 , 178 111 , 180 113 , 183 114 , 185 116 , 188 118 , 191 119 , 193 121 , 196 122 , 198 ========================== 仅仅从数字规律而言,可以看出前后两个石头数量之差为1,2,3...是一个等差数列, 而且从取数的规律而言,可以采用以下方式找到所有的先拿者输的情况(没有证明过,只是从规律上猜的) a=1, b=2,先拿者负 set.put(2) 下一行 a=2,由于元素2已经在set中了,跳过,同时set.remove(2) -- 以后再也不可能查到第2行了,所以从set里删掉它 a=3,set中没有元素3,所以 a=3, b=a+2=5是一个新方案,set.put(5) 下一行 a=4,set中没有元素4,所以 a=4, b=a+3=7是一个新方案,set.put(7) 下一行 a=5,由于元素5已经在set中了,跳过,同时set.remove(5) 下一行 a=6,set中没有元素6,所以 a=6, b=a+4=10是一个新方案,set.put(10) 下一行 a=7,由于元素7已经在set中了,跳过,同时set.remove(7) ... 这样依次类推,一直到a, b超过所要求得范围为止,这样,至少不需要1Gx1G这么多的内存。
abc130314 2014-04-29
  • 打赏
  • 举报
回复
引用 9 楼 yyfhz 的回复:
试着做了20*20的矩阵,看起来先取者负的情况是有规律的(很像在一条直线上)
这是我根据9楼总结出来的规律。 1:1,2 2:3,5 3:4,7 4:6,10 5:8,13 6:9,15 7:11,18 8:12,20 9:14,23 10:16,26 赢的方法是: 如果 |a-b| = 4 1、拿到只剩 a:6,b:10,如果开局就这样,那必输 1.1、如果乙拿了之后,|a-b|<4, 那甲从AB都拿,总能凑成1:1,2、2:3,5、3:4,7 1.2、如果乙拿了之后,|a-b|>=4,那甲只从一堆拿,总能凑成1:1,2、2:3,5、3:4,7 2、如果拿不到 a:6,b:10,如 a:1,b:5,那就只从一堆拿,总能凑成1:1,2、2:3,5、3:4,7
		TreeSet<Integer> set = new TreeSet<Integer>();
		for (int i = 1; i < 100; i++) {
			set.add(i);
		}
		for (int i = 1;; i++) {
			int a = set.first();
			int b = a + i;
			System.out.println(i + ":" + a + "," + b);
			set.remove(a);
			if (!set.remove(b)) {
				break;
			}
		}
湘水友独 2014-04-29
  • 打赏
  • 举报
回复
方法一:从任意一堆取任意数量的石子?那你全取了不就赢了么
lliiqiang 2014-04-29
  • 打赏
  • 举报
回复
我的思路是用递归.从(1,1)推到(1,2).......
mazhifei188 2014-04-29
  • 打赏
  • 举报
回复
根据具体的 石子数量,算法不同。
swjtueducn2 2014-04-29
  • 打赏
  • 举报
回复
不管是什么结果,先要考虑“最优策略”是个什么策略,考虑周全了,就实现算法,好的算法就会有好的结果,但是不能保证此游戏结果的一致性,没有什么条件限定的条件下,游戏总会有输有赢的。
oFengKun 2014-04-29
  • 打赏
  • 举报
回复
看来这么多人对这个题目感兴趣
yyfhz 2014-04-28
  • 打赏
  • 举报
回复

试着做了20*20的矩阵,看起来先取者负的情况是有规律的(很像在一条直线上)
yyfhz 2014-04-28
  • 打赏
  • 举报
回复
Sorry没仔细看题目,题目中的石头数范围太大,直接拿上面的算法存储空间根本不够,还要再想想
yyfhz 2014-04-28
  • 打赏
  • 举报
回复
大致的思路: 假设有两堆石头,数量分别为a和b,定义函数F(a,b)=true表示先拿者胜,F(a,b)=false表示先拿者负。 则有F(a,b)=F(b,a),F(0,b) =true (b>0), F(a,a)=true (a>0), F(1,2)=false 然后,对于F(a,b)这样的函数,如果存在F(a-t, b-t)=false,或者存在F(a-t,b)=false,或者存在F(a,b-t)=false,则F(a,b)=true,否则F(a,b)=False。 ==================================================================== 具体的算法可以类似用筛数法求质数集合的方法

int Result[aMax+1, bMax+1]={{0,...,0},{0,...,0}}; //0表示待求结果,1表示先拿者胜,-1表示先拿者负
for (int a=0; a<=aMax, a++) {
  for (int b=0; b<=bMax; b++) {
    if (a==0&&b==0) {
       continue;  //跳过没有石头的状态
    }
    int currentResult=Result[a,b];
    if (currentResult!=0) {
      continue;
    }
    if (a==b) {
      currentResult = 1;
    }
    if (a==0) || (b==0) {
      currentResult = 1;
    }
    if (currentResult==0) {
      currentResult=-1;
    }
    if (currentResult==-1) {
      //将可以一步走到当前状态的所有状态都设置为true
      for (int t=1; a+t<=aMax&&b+t<=bMax; t++){
        Result[a+t,b+t]=1;
        Result[b+t,a+t]=1;
      }
      for (int t=1; a+t<=aMax; t++){
        Result[a+t,b]=1;
        Result[b,a+t]=1;
      }
      for (int t=1; b+t<=bMax; t++){
        Result[a,b+t]=1;
        Result[b+t,a]=1;
      }
    }
    //设置当前状态变量
    Result[a,b]=currentResult;
    Result[b,a]=currentResult;
  }
}
没有调试过,不过大致的思路应该没错
panhaichun 2014-04-28
  • 打赏
  • 举报
回复
引用 11 楼 yyfhz 的回复:
[quote=引用 10 楼 panhaichun 的回复:] 太简单啊,如果2堆石子数量相差为1,先手必输,否则赢。 如果相等,则先手赢。 如果不相等,先拿的拿走几个石头,保证2堆数量相差为一。 以后每次不管对手怎么拿,自己拿了之后都保证相差为1,定赢
你确定? 两堆石子,数量分别是(2,3),先手必输?先手者可以再大的那个堆里面拿走2个,变成(2,1),不就赢了吗? 两堆石子,数量分别为(5,3),先手能赢?[/quote] 有道理,再想想
panhaichun 2014-04-28
  • 打赏
  • 举报
回复
我上楼说的有漏洞。 如果2堆石子数量相差为1,先拿输,否则赢。 数量相等,先拿赢。 如果数量相差不为1,那么先拿的每次拿走石头之后都保证数量相差1,定能赢。如果对方把一堆拿完,则把另一堆拿完。 如果数量相差为1,那么后拿的人可以保证每次拿完数量相差为1,后拿者赢
yyfhz 2014-04-28
  • 打赏
  • 举报
回复
引用 10 楼 panhaichun 的回复:
太简单啊,如果2堆石子数量相差为1,先手必输,否则赢。 如果相等,则先手赢。 如果不相等,先拿的拿走几个石头,保证2堆数量相差为一。 以后每次不管对手怎么拿,自己拿了之后都保证相差为1,定赢
你确定? 两堆石子,数量分别是(2,3),先手必输?先手者可以再大的那个堆里面拿走2个,变成(2,1),不就赢了吗? 两堆石子,数量分别为(5,3),先手能赢?
panhaichun 2014-04-28
  • 打赏
  • 举报
回复
太简单啊,如果2堆石子数量相差为1,先手必输,否则赢。 如果相等,则先手赢。 如果不相等,先拿的拿走几个石头,保证2堆数量相差为一。 以后每次不管对手怎么拿,自己拿了之后都保证相差为1,定赢
二萌无双 2014-04-27
  • 打赏
  • 举报
回复
参照<编程之美>1.13中的解法。。。
Inhibitory 2014-04-27
  • 打赏
  • 举报
回复
你没办法保证你第一次拿多少就一定会赢,因为数量不一样是第一次拿的数量也不一样。 这个问题可以用状态树来考虑,每拿一次都要调整拿法。 可以用下面的算法找到能赢的各种拿法,然后拿的时候按照对应的状态调整拿法才能保证赢。 如有7:3,那么第一次就会有很多中拿法,从1个到7个: 第一次拿1个时 a: 左1 b: 右1 c: 左右都1 第一次拿2个时 a: 左2 b: 右2 c: 左右都2 第一次拿3个时 a: 左3 b: 右3 c: 左右都3 第一次拿4个时 a: 左4 b: 右4(不能) c: 左右都4(不能) ... 一直到拿7个。 每拿一次都判断一下状态,如果时 x:0, 0:x, x:x时,接下来拿的人就一定会赢。 每拿一次后就递归的开始按上面的算法拿。
幸福村1 2014-04-26
  • 打赏
  • 举报
回复
引用 2 楼 Inhibitory 的回复:
你取走后如果没有空,那么就要一堆比另一堆多一颗就可以了。
我就这个思路啊,问的就是双方石子用什么策略拿,比如一堆石头7个,另一堆4个。这种组合,甲最优走出(6,3),乙怎么走都是甲赢,那么石头任意的话,双方最优策略最后如何判断谁必输必胜?
zhang1317725 2014-04-21
  • 打赏
  • 举报
回复
有意思吗
Inhibitory 2014-04-21
  • 打赏
  • 举报
回复
你取走后如果没有空,那么就要一堆比另一堆多一颗就可以了。
加载更多回复(1)

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧