海盗分金问题

这是一个很有趣的题目,当然博弈本身就是一件有趣的事情。


描述:有n个海盗劫得了窖藏的m块金子,并准备瓜分这些战利品。按照古老流传下来的分金法则,由最厉害的一名海盗提出一个分金方案,假如有不小于一半的海盗(包括自己)支持这个方案,则按这个方案分,否则把这个海盗扔进海里,重复由下一个厉害的海盗提出方案。        大家都知道,所有海盗都是贪婪的,虽然他们都乐于看到自己的同伴被扔进海里,但是他们还是希望在保命的前提下分的最多的金子,现在已经按海盗的厉害程度进行了编号,最厉害的海盗为最大号,依次到小,那么第 k 号海盗能分的多少金。

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=994


分析:这个问题的思考过程是一个很有趣的过程。


(引用)(大牛的分析)

数学的逻辑有时会导致看来十分怪异的结论。一般的规则是,如果逻辑推理没有漏洞,那么结论就必定站得住脚,即使它与你的直觉矛盾。 1998年9月,加利福尼亚州帕洛阿尔托的Stephen M. Omohundro寄给我一道难题,它恰好就属于这一类。这难题已经流传了至少十年,但是Omohundro对它作了改动,使它的逻辑问题变得分外复杂了。 

先来看看此难题原先的形状。10名海盗抢得了窖藏的100块金子,并打算瓜分这些战利品。这是一些讲民主的海盗(当然是他们自己特有的民主),他们的习惯 是按下面的方式进行分配:最厉害的一名海盗提出分配方案,然后所有的海盗(包括提出方案者本人)就此方案进行表决。如果50%或更多的海盗赞同此方案,此 方案就获得通过并据此分配战利品。否则提出方案的海盗将被扔到海里,然后下提名最厉害的海盗又重复上述过程。
所有的海盗都乐于看到他们的一位同伙被扔进海里,不过,如果让他们选择的话,他们还是宁可得一笔现金。他们当然也不愿意自己被扔到海里。所有的海盗都是有 理性的,而且知道其他的海盗也是有理性的。此外,没有两名海盗是同等厉害的——这些海盗按照完全由上到下的等级排好了座次,并且每个人都清楚自己和其他所 有人的等级。这些金块不能再分,也不允许几名海盗共有金块,因为任何海盗都不相信他的同伙会遵守关于共享金块的安排。这是一伙每人都只为自己打算的海盗。 最凶的一名海盗应当提出什么样的分配方案才能使他获得最多的金子呢? 
为方便起见,我们按照这些海盗的怯懦程度来给他们编号。最怯懦的海盗为1号海盗,次怯懦的海盗为2号海盗,如此类推。这样最厉害的海盗就应当得到最大的编号,而方案的提出就将倒过来从上至下地进行。 

分析所有这类策略游戏的奥妙就在于应当从结尾出发倒推回去。游戏结束时,你容易知道何种决策有利而何种决策不利。确定了这一点后,你就可以把它用到倒数第 2次决策上,如此类推。如果从游戏的开头出发进行分析,那是走不了多远的。其原因在于,所有的战略决策都是要确定:“如果我这样做,那么下一个人会怎样做?”

因此在你以下海盗所做的决定对你来说是重要的,而在你之前的海盗所做的决定并不重要,因为你反正对这些决定也无能为力了。 

记住了这一点,就可以知道我们的出发点应当是游戏进行到只剩两名海盗——即1号和2号——的时候。这时最厉害的海盗是2号,而他的最佳分配方案是一目了然 的:100块金子全归他一人所有,1号海盗什么也得不到。由于他自己肯定为这个方案投赞成票,这样就占了总数的50%,因此方案获得通过。 

现在加上3号海盗。1号海盗知道,如果3号的方案被否决,那么最后将只剩2个海盗,而1号将肯定一无所获——此外,3号也明白1号了解这一形势。因此,只 要3号的分配方案给1号一点甜头使他不至于空手而归,那么不论3号提出什么样的分配方案,1号都将投赞成票。因此3号需要分出尽可能少的一点金子来贿赂1 号海盗,这样就有了下面的分配方案: 3号海盗分得99块金子,2号海盗一无所获,1号海盗得1块金子。 
4号海盗的策略也差不多。他需要有50%的支持票,因此同3号一样也需再找一人做同党。他可以给同党的最低贿赂是1块金子,而他可以用这块金子来收买2号 海盗。因为如果4号被否决而3号得以通过,则2号将一文不名。因此,4号的分配方案应是:99块金子归自己,3号一块也得不到,2号得1块金子,1号也是 一块也得不到。

5号海盗的策略稍有不同。他需要收买另两名海盗,因此至少得用2块金子来贿赂,才能使自己的方案得到采纳。他的分配方案应该是:98块金子归自己,1块金子给3号,1块金子给1号。
 
这一分析过程可以照着上述思路继续进行下去。每个分配方案都是唯一确定的,它可以使提出该方案的海盗获得尽可能多的金子,同时又保证该方案肯定能通过。照 这一模式进行下去,10号海盗提出的方案将是96块金子归他所有,其他编号为偶数的海盗各得1块金子,而编号为奇数的海盗则什么也得不到。这就解决了10 名海盗的分配难题。

Omohundro的贡献是他把这一问题扩大到有500名海盗的情形,即500名海盗瓜分100块金子。显然,类似的规律依然成立——至少是在一定范围内成立。事实上,前面所述的规律直到第200号海盗都成立。 200号海盗的方案将是:从1到199号的所有奇数号的海盗都将一无所获,而从2到198号的所有偶数号海盗将各得1块金子,剩下的1块金子归200号海盗自己所有。 

乍看起来,这一论证方法到200号之后将不再适用了,因为201号拿不出更多的金子来收买其他海盗。但是即使分不到金子,201号至少还希望自己不会被扔进海里,因此他可以这样分配:给1到199号的所有奇数号海盗每人1块金子,自己一块也不要。

202号海盗同样别无选择,只能一块金子都不要了——他必须把这100块金子全部用来收买100名海盗,而且这100名海盗还必须是那些按照201号方案将一无所获的人。由于这样的海盗有101名,因此202号的方案将不再是唯一的——贿赂方案有101种。

203号海盗必须获得102张赞成票,但他显然没有足够的金子去收买101名同伙。因此,无论提出什么样的分配方案,他都注定会被扔到海里去喂鱼。不过, 尽管203号命中注定死路一条,但并不是说他在游戏进程中不起任何作用。相反,204号现在知道,203号为了能保住性命,就必须避免由他自己来提出分配 方案这么一种局面,所以无论204号海盗提出什么样的方案,203号都一定会投赞成票。这样204号海盗总算侥幸拣到一条命:他可以得到他自己的1票、 203号的1票、以及另外100名收买的海盗的赞成票,刚好达到保命所需的50%。获得金子的海盗,必属于根据202号方案肯定将一无所获的那101名海 盗之列。 

205号海盗的命运又如何呢?他可没有这样走运了。他不能指望203号和204号支持他的方案,因为如果他们投票反对205号方案,就可以幸灾乐祸地看到 205号被扔到海里去喂鱼,而他们自己的性命却仍然能够保全。这样,无论205号海盗提出什么方案都必死无疑。206号海盗也是如此——他肯定可以得到 205号的支持,但这不足以救他一命。类似地,207号海盗需要104张赞成票——除了他收买的100张赞成票以及他自己的1张赞成票之外,他还需3张赞 成票才能免于一死。他可以获得205号和206号的支持,但还差一张票却是无论如何也弄不到了,因此207号海盗的命运也是下海喂鱼。

208号又时来运转了。他需要104张赞成票,而205、206、207号都会支持他,加上他自己一票及收买的100票,他得以过关保命。获得他贿赂的必 属于那些根据204号方案肯定将一无所获的人(候选人包括2到200号中所有偶数号的海盗、以及201、203、204号)。
 
现在可以看出一条新的、此后将一直有效的规律:那些方案能过关的海盗(他们的分配方案全都是把金子用来收买100名同伙而自己一点都得不到)相隔的距离越 来越远,而在他们之间的海盗则无论提什么样的方案都会被扔进海里——因此为了保命,他们必会投票支持比他们厉害的海盗提出的任何分配方案。得以避免葬身鱼 腹的海盗包括201、202、204、208、216、232、264、328、456号,即其号码等于200加2的某一方幂的海盗。 
现在我们来看看哪些海盗是获得贿赂的幸运儿。分配贿赂的方法是不唯一的,其中一种方法是让201号海盗把贿赂分给1到199号的所有奇数编号的海盗,让 202号分给2到200号的所有偶数编号的海盗,然后是让204号贿赂奇数编号的海盗,208号贿赂偶数编号的海盗,如此类推,也就是轮流贿赂奇数编号和 偶数编号的海盗。 

结论是:当500名海盗运用最优策略来瓜分金子时,头44名海盗必死无疑,而456号海盗则给从1到199号中所有奇数编号的海盗每人分1块金子,问题就 解决了。由于这些海盗所实行的那种民主制度,他们的事情就搞成了最厉害的一批海盗多半都是下海喂鱼,不过有时他们也会觉得自己很幸运——虽然分不到抢来的 金子,但总可以免于一死。只有最怯懦的200名海盗有可能分得一份脏物,而他们之中又只有一半的人能真正得到一块金子,的确是怯懦者继承财富。


海盗决策的三个标准:

保命, 拿更多金币, 杀人. 这三条原则的优先级依次递减, 当然, "遵守规则"这一原则的优先级是最高的.

稳定状态和不稳定状态:

如果当n和m的组合使得最先决策的人(编号为n)不会被丢下海, 即游戏会立即结束, 就称这个状态时"稳定的". 反之, 问题会退化为n-1和m的组合, 直到达到一个稳定状态, 所以乘这种状态为"不稳定的".

哪些人能活下来:

当n<2m+3时, 所有人都能活下来. 考虑第2m个人, 当然是给自己1个金币, 然后给其他所有编号为偶数的人每人1个即可; 考虑第2m+1个人, 现在他需要牺牲自己的利益而活命, 把m个金子分给上一轮没有分到金子的人, 自己分0个, 然后自己投自己1票, 超过半数, 活下来; 考虑第2m+2个人, 同样自己分0个, 这时上一轮推导没有分到金子的有m+1个人, 他可以选择在这些人中选m个每人1个金币, 然后加上自己1票, 刚好半数, 活下来;
当n=2m+3时, 考虑第n个人, 不幸的, 必死; (此时是不稳定状态)
当n=2m+4个, 因为第2m+3个人为了保命一定会支持他, 所以他能活下来. (此时是稳定状态)
...
这样可以找到规律, 当n=2m+2^k时(k=1,2,...)是稳定状态, 游戏会立即结束.

补充, 为什么是n=2m+2^k:

因为假设你是第2m+2^k个人, 并假设轮到所有编号大于2m的人决策时都会把m个金币分给前2m个中的m个人(因为下述的不确定性, 这样分配不会影响结果), 向前推理, 到第2m+2^(k-1)个人决策时, 前面会死的人为了保命一定会支持2m+2^(k-1), 而反对所有大于2m+2^(k-1)的人, 因为一旦轮到2m+2^(k-1)决策, 他们都能活下来, 又由"获得金币数量相同的情况下尽量杀人"这一原则可知他们一定会尽可能让后面的人死, 所以你, 2m+2^k必须攒够半数票, 而这个空缺就由2m+2^(k-1)到2m+2^k之间的人, 以及2m+2^k自己来填补, 所以2m+2^k是稳定的.

每个人能获得多少金币:

考虑n<2m+1时, 第n个人总能分到金币, 于是与n奇偶性相同的人都能获得1个金币.
当n=2m+1时, 第n个人为了保命而分得0个金币, 与决策者编号奇偶性相同的人都能获得1个金币.
当n=2m+2时, 即第一个满足n=2m+2^k的状态, 此时是稳定状态, 第n个人一定为了保命而分得0个金币, 而因为在上一轮推导中没有获得金币的人数大于m, 所以这时只有上一轮获得过金币的人的金币数量确定是0, 其他人的金币数量都是不确定的.

金币数量的不确定性:

由上面的推理可知, 当n=2m+2时, 上一轮推理没有分到金币的人的金币数量首次具有不确定性,
为什么说当n=2*m+2时不确定呢,首先还是拿上面的例子,500个海盗,100金。
那么第201海盗的时候,自己不分,给前面1 - - 199中奇数的各1金。
到第202海盗的时候,除了自己刚好需要100个人支持,如果按照前面的当然给2 -- 200中偶数号海盗,但是假如他给201号海盗的话,他也会支持,因为假如202号丢进海里,自己分的话他是得不到的,所以他能拿到金子,当然也支持202号,所以这样就有101个人只要得到金子就一定会支持,所以出现了确定性,  并且在n>2m+2时, 这种不确定性一定会延续下去, 轮到因为n号决策者之前的一个人决策时, 那个人肯定分不到金币了, 所以在上一轮推理中没有分到金币的人的个数一定大于m.

本题解法:

由上述推理可知, 当n<2m+1时好办所有与n奇偶性相同的人都得1个金币; 当n=2m+1时除了第n个人得0个, 其他与n奇偶性相同的人都得1个金币; 当n>=2m+2^k时, 设使满足n>=2m+2^k的最大的k为k', 则第2m+2^k'个人是第一个稳定状态, 他分0个金币, 编号x<2m+2^k'的人或者因为上一轮推理分到金币, 所以这一轮没有金币, 或者是因为上述的不确定性而没有金币(题目要求输出0), 反正都是0个, 而所有x>2m+2^k'都处于不稳定状态, 必死.


代码:

#include <cstdio>
int fac[15]= {2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,k,ans=-1;
        scanf("%d%d%d",&n,&m,&k);
        if(2*m+1>=n)
        {
            if(n==k)
            {
                ans=m-((n-1)/2);
            }
            else if(n%2==k%2)
                ans=1;
            else
                ans=0;
        }
        else
        {
            n-=2*m;
            int ok=0;
            for(int i=0; i<14; i++)
                if(n==fac[i])
                {
                    ans=0;
                    ok=1;
                    break;
                }
            if(ok==0)
            {
                for(int i=1; i<14; i++)
                {
                    if(n<fac[i])
                    {
                        if(k>2*m+fac[i-1] && k<2*m+fac[i])
                            ans=-1;
                        else
                            ans=0;
                        break;
                    }
                }
            }
        }
        if(ans==-1)
            printf("Thrown\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值