您的位置:首页 > 编程语言 > C#

博弈游戏(2)-c#求解-英雄会在线编程题目

2014-03-05 11:44 232 查看
这道题写出思路后,好多人在问,可能当时写的匆忙,不够完善,今天重新修改下。

看看时间还早,在写一篇博客,记录下博弈游戏2的解法。

博弈游戏(2)

发布公司:
有 效 期:
赛 区:

CSDN
2014-02-26至2014-03-28
北京

难 度 等 级:
答 题 时 长:
编程语言要求:











120分钟
C C++ Java C#

题目详情

AB两个人玩一个取石子游戏,一共有n (n>=2)个石子,要求和规则如下:

(1)第一次由 A先取,至少取一个石子,至多取(n-1)个。

(2) 两个人轮流取石子,后面一个人取石子的数目至少为1,至多为上一个人石子数的2倍。

(3) 取到最后一颗石子的人为胜利者

(4) 两个人足够聪明。

问A是否有必胜策略?如果有的话,第一次他可以取多少个石子?如果有必胜策略,返回A第一次可以取得石子数可以保证必胜,并且如果有多个答案输出最小的。如果A无法必胜,输出-1。(2 <= n <= 100000000)

这道题还是有点意思的,最开始的思路就是递归调用,然后缓存中间值,得到结果。先说说这个不成熟的思路吧。

递归函数很简单,传入参数为:还剩余多少个石子,当前谁来取,之前的人取了多少个,这样三个参数。

函数里面就循环以下,看看取多少个石子必胜。

当把函数写完,测试调用,速度比较快,很快地出了答案,当把数据改为较大的时候,问题来了,报错,函数调用的次数太多,而且时间很长啊,看来此路不通啊,只能另想它法了。

经过了前面的小数据测试,发现了一些规律,形成了一些新的思路。

/********************************************************/

2014/7/23

重新整理下,发现之前写的比较模糊,很多人没看明白。

我们把当前石子数分为必胜数和必败数。

必胜数:指在不取尽石子的情况下,且满足基本条件下,一定有方法必然胜利。比如:4,6,7.....

必败数:指在不取尽石子的情况下,且满足基本条件下,无论如何取,均会失败。比如2,3,5.......

要分析这个是数是不是必胜数,先从分析必败数考虑,假设当前是一个必败数K,那么前一个人面前的数假设是N,那么有

K>(N-K)*2;这个公式是由条件不能取尽石子限制的,并且最多取前一个人取得石子的2倍得出来的,否则对于K,超出这个范围的取值区间,他一定可以把剩下的K个石子一次取完。

这里通过K得到一个N的上限值,但是,这是在分析尽量取到只剩下K的情况,比如说K=5时,我们分析出来N=7;也就是说,7个石子的时候,我们一步拿走2个石子,使剩下的直接面临5个石子的情况,他必然输。但是我们也可能多步达到取走石子,使后面的人面临数字5,也就是说,我一定要抢走数字6,后面的一个人就会面对数字5,这时候我们发现,抢数字6是不是相当于抢数字1?因此:从6开始标注到7,重新标注后就是1(6),2(7),根据前面初始化的数据,2后面下一个必败数字是3,必然抢不到1,所以可以得到3-2+7=8,也就是K=5的时候,下一个必然失败的数字是K=8.

K=3,N=4,重新标注区间【4,4】为[1,1],此区间的下一个必败点是【2】,换算到前面的区间,得到下一个K=2-1+4=5

K=5,N=7,重新标注区间【6,7】为[1,2],此区间的下一个必败点是【3】,换算到前面的区间,得到下一个K=3-1+6=8

K=8,N=11,重新标注区间【9,11】为[1,3],此区间的下一个必败点是【5】,换算到前面的区间,得到下一个K=5-1+9=13

依次类推,很容易得到所有的K,而且这个必败点的数量很少。

那么第一步就很容易得出,这个数是不是必败的数了。

/**************************************************************************/

规律1:当前取石子的人,如果不能取尽石子,那么他应该取得石子数不超过总数的1/3,否则后面一个人肯定可以取尽石子。

这个地方需要注意:如果在他取石子的范围内,如果存在某个数字,让对方去取,无论对方如何取数,都会失败,那么我们把这个必然失败的数字标记为必然失败点。因此,第一步就是找出所有必然失败的点。这个很容易找到。

其次,如果能让对方面临必然失败的点,那么自己面临的这个数必然胜利。

如果是必然胜利,那么我们再进行下一步考虑,即最少取几个石子的问题。

试想:如果当前取石子的人,在规律一的范围内,取走某一些石子,使剩下的石子,让后面的人不管怎么取,都必输,那她自然必胜了。

有了前面的分析,现在一个问题是,最小可以取多少了?

为了说明此问题,我们先用一个数组来表示多少个石子,能否必胜,以及最少取多少。int[] array=new int[n+1],

array[1],表示只剩一个石子,则必然胜利。array[1]=1

array[2],必输,array[2]=-1

array[3],必输,array[3]=-1

array[4],必胜,]array[4]=1,表示最少取一个,必然胜利

。。。。

后面的根据规律一,很容易得出答案,array
是不是必输(-1),

如果不是,可以根据规律一得到一个答案,即取到的石子数,但不一定是最小的哦。

这时候就要考虑第二个思路了:

规律二:我既然去了一个较大的数字后,剩下的,后取者必输,那么我能不能取一个较小的数,抢到必输数字的前一个数字,那么,后取者一定必输?这时候我们发现规律的,这不就是降次处理了嘛?

举例来说明:

array【1】=1

array【2】=-1

array【3】=-1

array【4】=1

array【5】=-1

array【6】=1

array【7】=2

array【8】=-1

array【9】=1

array【10】=2

array【11】=3

array【12】=1,当有12个石头的时候,相当于从9开始,到12,谁先抢到9,那么剩下8个石头,给谁都必输,那么冲12到9,就相当于4到1,即4个石头,最少取1个必然能抢到1,所以,依次类推,就可以求出array【n】=多少了

在实际编写过程中,经过测试,发现,n很大100000000,那么求出array
的时间还是接近3秒。

其实有了上面的分析过程,我们发现,后面基本上都是迭代前面的逻辑,所以,完全没有必要全部求出array
,只需要不停迭代降次处理,直到满足规律一,或者降次后能够满足规律二,就可以返回答案了。

这个代码量也不是特别的多,提交没问题。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: