拿石子问题

mathe 2001-12-16 10:51:25
现有石子 n 堆, 每堆石子若干. 同时给一自然数 M.
甲乙两人交替做如下操作: 任选一堆, 将其任意分为至多M堆(但至少两堆).
最后无堆可分的一人为输.

问题: 何时先走(或后走)一方有必胜策略, 策略为何?
...全文
19372 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
mathe 2002-01-16
  • 打赏
  • 举报
回复
OK,I have found the problem of intfree program,

g[x ^ f[i] ^ f[n - i]] = true; //not n-i because some integer has been used to get x.
fill(step + 1, i, n - i, x ^ f[i]);

But it seems intfree has understood my algorithm.
mathe 2002-01-16
  • 打赏
  • 举报
回复
m>3时的结果时intfree先给出来的?intfree() 回复于2002-1-15 12:33:56
看来我是给错分了,这样吧,也给intfree()50分,然后就等你们解决m=3时的情况了。
congling 2002-01-15
  • 打赏
  • 举报
回复
如果是2,5,9,你的算法告诉我们是Win的,但是如何Win?
intfree 2002-01-15
  • 打赏
  • 举报
回复
搞错了,搞错了,昨天我给的程序复杂度为O(max^3),不是O(max^4)

intfree 2002-01-15
  • 打赏
  • 举报
回复
>>m=3并不满足定理,你没有经过证明如何可以用?

你是说什么定理啊?我还是谈谈我的解法吧,

我的想法就是,在已知m的条件下,找到一个函数f(x),
使得对于任意状态s = {a1, a2, ..., an},定义函数g(s),其中
g(s) = f(a1) xor f(a2) xor ... xor f(an)
若g(s) = 0,则对于状态s,后走有必胜策略,否则,先走有必胜策略,

至于f(x)怎么求,你可以看看我的程序,原理嘛,可以看看mathe()在
http://www.csdn.net/expert/topic/348/348397.shtm
中的解答。

当m = 2时,可以证明:f(n) = 1 - (n mod 2),
当m > 3时,可以证明:f(n) = n - 1,
当m = 3时,就很难找到一个简单的表达式了,可以肯定的是
f(1) = 0
1 <= f(n) <= n - 1, (n > 1)
f(n) != f(n - 1)
f(n) != f(n - 2k),(k > 0)
等等,

当m = 3时,昨天我给的程序复杂度为O(max^4),
但我觉得即便m = 3时,找不出f(n)的简单的表达式,也应该可以找到一个更快的算法。
intfree 2002-01-15
  • 打赏
  • 举报
回复
刚才想编一个搜索来验证结果,但我的搜索太慢了,石子数只能到24,

一个比较大的数据是 d = {10, 31},m = 3,我的结果是后走必胜,
如果这个结论没有错,我想程序就应该没问题了 :)


最后还要感谢mathe()的这道题目,它使我终于领悟了上次您在

http://www.csdn.net/expert/topic/348/348397.shtm

的解答。
congling 2002-01-15
  • 打赏
  • 举报
回复
m=3并不满足定理,你没有经过证明如何可以用?
mathe 2002-01-15
  • 打赏
  • 举报
回复
在m=3,intfree的code好像有问题呀,
{10,31}在m=3应该先手胜,先手可以将31转化为13,9,9

{10,31}-->{13,10,9,9} (等价于{13,10})
mathe 2002-01-15
  • 打赏
  • 举报
回复
呵呵,这道题目也差不多了,其实这一道题目我也没有解答,不过m=3的时候,应该是可以用计算机来解答的,至于m>3的情况,我也没有详细考虑,不过就太复杂了。在等几个小时给分吧
congling 2002-01-15
  • 打赏
  • 举报
回复
更进一步,在m确定的时候,其实要实现next(n),其实可以这样看
k^k=0
k^k^1=1
k^k^2=2
k^k^3=3
而在n>m的时候,设n=n1*m+k(0<=k<m),next(n)中缺少的个数是0,1,2,...k
我们可以证明m>3的时候,我们可以这样分法,m=3的时候,要看情况
因此在m>3的时候|n|=n,n=3的时候这个条件不一定成立
foxwizard 2002-01-15
  • 打赏
  • 举报
回复
唉,上面那个说了半天其实只有一个结论,
就是必胜策略肯定应当是先去分多的那一堆,
把多的那一堆留给自己分总是有利的。
比如21111111111111,分到2的人当然赢了。
3222222222222222,也是分到3的人可以控制局面,
4333333333333333,也是如此。
将8544分成75441总比分成54444要好……因为要把最大的那一堆尽可能地留给自己是最好的
foxwizard 2002-01-15
  • 打赏
  • 举报
回复

M=2时,就没什么好说了,一算就出来了,所以下面都是针对M>2说的。

在某一特定状态下,必胜策略必然存在。
如何让对手无法获得此状态呢?唯一的办法就是让对手没有选择。
我所能想到的情况就是N堆石子,每堆最多为2粒,这样对手就不能够有选择的机会。
如果有任一堆为2粒以上,则主动权可能因此落入对手手中。

所以必佳策略的对抗必然都会经过一个所有石子被分为2粒一堆的过程。

如果在起始状态下已经有1粒一堆的情况,则此堆可忽略不计。

在竞赛中,双方都应当避免在还有2粒以上一堆的情况下,先将别的分成一粒一堆。
因为这样就等于失去选择权。

所以情况可以回溯成,N堆石子,共有Y粒,除去已经是单粒成堆的情况,剩下的数目为X粒。那么,谁先将X粒分成X/2,或(X-1)/2,或X/2-1堆时,就决定了胜负。具体谁胜谁负
需要视X/2或(X-1)/2是奇数还是偶数了。

所以情况又可以回溯成,N堆石子,平分为3粒/堆的情况。
也就是说,对有必胜策略的一方,应当尽量少分石子,以控制主动。

大致思路如此,如何写成算式,好像俺还说不清楚,但看不出上面说的有啥错吧。

congling 2002-01-15
  • 打赏
  • 举报
回复
终于明白了!
因为分石子,可以看为取石子中的空间(n=n-1,m=m-1):

如果m->∞,|n|=n, 但是如果m不是无穷大,那么|n|!=n,因为他不可以保持这么多的状态,
比如
m=2,n=3,
next(n)={|2|,|(1,1)|,|1|}={0,1,2},|3|=3;
m=2,n=4,
next(n)={|3|,|(1,2)|,|1,1|,|2|}={3,2,0,2},|4|=1

因此在|{1,4}|=|1|^|4|=0,失败的情况

一般的,当n<=m的时候,next(n)=n,但是如果n>m的时候next(n)不一定为n
但是要求保持|n1|^|n2|.....^|nk|=0,并不需要next(n)=n



congling 2002-01-15
  • 打赏
  • 举报
回复
来一些理论冲冲电,如果不是还不敢用!


节选http://bbs.whnet.edu.cn/cgi-bin/bbscon?board=Mathematics&file=M.940936045.A&num=402

一个集合A的下一步走法集Next(A)
等效值是用递归方法定义,
1:若Next(A)不存在则|A|=0
2:若Next(A)存在记
Next(A)={ a1,a2,...,an },next(A)={ |a1|,|a2|,...|an| }

|A|=min{x| x不属于next(A),x为非负整数}

等效值的意义在于,如果|A|=0,表明下一步无法走,这个时候先驱者失败,
如果|A|!=0,表明下一步可以走,而且能够胜利的使到后续者无法在走


定义集合的加法为:
若A=(a1,a2,...,an),B=(b1,b2,...,bm);
A+B=(a1,a2,...an,b1,b2,...bm);

定理1: |A+B|=|A|^|B| (^为异或符号)
//这里没有证明基础条件,不完整
用数学归纳法证明:
设n=Count(A)+Count(B),
n=k,|A+B|=|A|^|B|
n=k+1,
即在A/B中添加一个元素,由于AB对称,我们选择A
这个元素为a(k+1)
先证|A|^|B| 不属于next(A+B),
由于在n=k的时候,|A|^|B|=|A+B|,按照定义不属于next(A+B)
因此如果|A|^|B|属于next(A+B),一定是|a(k+1)+B|=|A|^|B|导致的
而由归纳法可得
|a(k+1)+B|=|a(k+1)|^|B| -->|a(k+1)|^|B|=|A|^|B| -->|A1|=|A| 此与等效值定义矛盾
因此,|A|^|B| 不属于next(A+B)

再证 x<|A|^|B| 时 x属于next(A+B)
若x<|A|^|B|则
x^|A|<|B|, x^|B|<|A| 总有一式成立
不防设 x^|A|<|B|,因为|B|!=0,根据定义,
存在B1属于Next(B)且|B1|=x^|A|;(|B|不等最小值原理)
于是由归纳法可得
x=|A+B1|属于next(A+B)

综上所述,由等效值定义得:
|A+B|=|A|^|B|
证毕。


这个证明有一些漏洞:就是没有证明基础条件(单元素集合的AB),不完整,还请mathe/intfree等高手们证明一把!

但是通过他我们可以知道,(n1-1)^(n2-1)....^(nk-1)=0(因为这里每行可取的项只有nk-1个)的定理,对于m=3使适应的。


congling 2002-01-15
  • 打赏
  • 举报
回复
9分成441就可以了看来的确有点正确的意思偶!
mathe 2002-01-14
  • 打赏
  • 举报
回复
对于M=2时,只能分为M份和可以分为不多于份是一回事。
建议先考虑M=2.
congling 2002-01-14
  • 打赏
  • 举报
回复
Sorry,我看出了题目,我上面的论证是针对只能分为M分的情况
intfree 2002-01-14
  • 打赏
  • 举报
回复

当m!=3时,这个程序到有点大材小用了,

#include <string.h>
#include <iostream.h>

const int MAX = 1000;
const int m = 3;
const int n = 2;
const int d[n] = {19, 24}; //d[i] < MAX
int f[MAX];
bool g[MAX];

int maxd()
{
int max = 0;
for (int i = 0; i < n; i ++)
{
if (d[i] > max)
{
max = d[i];
}
}
return max;
}

void fill(int step, int last, int n, int x)
{
if (step == m)
{
return;
}
for (int i = last; i < n; i ++)
{
g[x ^ f[i] ^ f[n - i]] = true;
fill(step + 1, i, n - i, x ^ f[i]);
}
}

void calc(int max)
{
memset(f, 0, sizeof(f));
for (int i = 2; i <= max; i ++)
{
memset(g, false, sizeof(g));
fill(1, 1, i, 0);
int k = 0;
while (g[k])
{
k ++;
}
f[i] = k;
}
}

void print()
{
int r = 0;
for (int i = 0; i < n; i ++)
r = r ^ f[d[i]];
cout << "first player: " << ((r == 0) ? "lose" : "win") << endl;
}

void main()
{
calc(maxd());
print();
}
congling 2002-01-14
  • 打赏
  • 举报
回复
对,在此时应该考虑胜利的情况,因为失败的情况可以不予考虑,这里要做修正
如果k1,k2,k3...,kn有2K个偶数先驱者胜利,先驱者胜利,否则先驱者失败

但是这个结论对后面的证明没有影响
mathe 2002-01-14
  • 打赏
  • 举报
回复
M=2时的结果已经不对了。
比如M=2,n=3;
3堆为{2,2,2}时先手胜(将一个2分为两个1)
3堆为{2,2,1}时后手胜。
你还可以参考一下http://www.csdn.net/expert/topic/424/424004.shtm,可能有帮助吧。
加载更多回复(14)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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