hdu 1538 A Puzzle for Pirates 博弈论

很经典的问题,思路转载自 http://blog.csdn.net/ACM_cxlove?viewmode=contents

题目:这是一个经典问题,有n个海盗,分m块金子,其中他们会按一定的顺序提出自己的分配方案,如果50%以上的人赞成,则方案通过,开始分金子,如果不通过,则把提出方案的扔到海里,下一个人继续。

首先我们讲一下海盗分金决策的三个标准:保命,拿更多的金子,杀人,优先级是递减的。

 

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

接下来我们从简单的开始分析:
如果只有两个人的话:那么2号开始提出方案,这时候知道不管提什么,他自己肯定赞成,过半数,方案通过,那么2号肯定把所有的金子都给了自己。
如果只有三个人的话:那么3号知道,如果自己死了,那么2号肯定能把所有金子拿下,对于1号来说没有半点好处。
那么他就拿出金子贿赂1号,1号拿到1个金子,总比没有好,肯定赞成3号,剩下的3号拿下。
如果只有四个人的话:那么4号知道,如果自己死了,那么1号拿到1个金子,2号什么都没有,3号拿下剩下的金子。
那他就可以拿出部分金子贿赂2号,2号知道如果4号死了,自己将什么都没有,他肯定赞成4号。
如此类推下去,貌似就是第一个决策的时候,与他奇偶性相同的人会被贿赂拿到1个金子,剩下的全归提出方案的人所有。
 
但是会有一个问题便是,如果金子不够贿赂怎么办。
情况1、我们首先归纳之前的,如果n<=2*m时候,前面与n相同奇偶性的得到1个金子,剩下的第n个人拿下。
情况2、如果n==2*m+1,第n个人拿出m个金子贿赂前面的m个人。自己不拿金子,这样刚好保证自己不死,这就是之前提到的优先级,首先得保命,如果自己拿了一个金子,那么前面就有一个人会反对,因为对于那个人,不管怎么样都分不到金子,则轮到第三个原则,杀人,肯定投反对票。
 
剩下来我们考虑,钱不够贿赂的情况:
我们将问题具体化:如果有500个海盗,只有100个金子,那么前面201个已经分析过了。
对于202号来说,自己不能拿金币,而贿赂上一轮没有拿到金币的101人中的100人就够了。
对于203号来说,需要102个人的支持,显然加上他自己,还需要101票,而金子不够贿赂,别人会反对,而达到杀人的目的。
对于204号来说,他知道一旦自己死了,203号是必死,抓住这点,203必然支持他,因为203号宁可不要金币,也要保住性命,所以204号把100个金币分给之前的100个人,然后203和他自己的两票保证自己不死。
对于205号来说,203,和204是不会支持他的,因为一旦205死了,他们不仅可以保住性命,而且还可以看着205死掉。所以205是必死
那么206呢,虽然205必死,会支持他,但是还是缺一票,所以必死。
对于207呢,205和206之前是必死,会支持他,但是加上自己以及100个贿赂名额,还是必死
对于208号,205,206.,207因为后面是必死的,肯定会支持208成功,那么208刚好能凑齐104票,得以保命。
 
以下我们猜想:n=2*m+2^k的情况下,是可以保命的,称为稳定状态,否则为不稳定状态,我们证明一下:
首先对于n来说,有m票贿赂,但是对于2*m+2^(k-1)以前必死的死,他们会支持2*m+2^(k-1),因为他们肯定拿不到钱,而且支持2*m+2^(k-1),另外根据杀人原则,希望之后的人都死,轮到2*m+2^(k-1)决策的时候保命就行了。
同理2*m+2^(k-1)到2*m+2^k之间的2^(k-1)-1个人来说,他们必死,所以必定支持2*m+2^k,加上m个金币贿赂的,加上他自己,刚好有m+2^(k-1)。这样刚好凑齐一半,可以不死。
证明完毕:2*m+2^k的人可以保命,否则必死。
 
我们考虑一下分金币情况:
情况3:对于第2*m+2^k个人来说,他可以保命,肯定分不到金子,而他手上的m个金子,可以贿赂m个人,但是具体是哪些人是不定的。则不管是不能分到金子,还是可能分不到金子的人来说,结果都为0。
情况4:对于2*m+2^(k-1)到2*m+2^k之间的来说,他们的决策是必死,而在他们决策的时候,其它人分得金币情况也为0。
 
我们来解释一下金币的不确定性:

金币数量的不确定性:由上面的推理可知, 当n=2m+2时, 上一轮推理没有分到金币的人的金币数量首次具有不确定性, 并且在n>2m+2时, 这种不确定性一定会延续下去, 轮到因为n号决策者之前的一个人决策时, 那个人肯定分不到金币了, 所以在上一轮推理中没有分到金币的人的个数一定大于m.

综合情况1,2,3,4便是本题的解,
代码如下:

 

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define I(x) scanf("%d",&x)
 6 #define ll __int64
 7 #define MAX 500000
 8 using namespace std;
 9 int f[15]={2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
10 void solve(int n,int m,int p)
11 {
12     if(n<=2*m){
13         if(n!=p&&(n%2==p%2)) printf("1\n");
14         else if(n==p) printf("%d\n",m-(n-1)/2);
15         else printf("0\n");
16         return ;
17     }
18     else if(n==2*m+1){
19         if(p<2*m&&(p&1)) printf("1\n");
20         else printf("0\n");
21         return ;
22     }
23     int t=n-2*m;
24     for(int i=0;i<14;i++){
25         if(t==f[i]){
26             printf("0\n");
27             return ;
28         }
29     }
30     for(int i=1;i<14;i++){
31         if(t<f[i]){
32             if(p>2*m+f[i-1]&&p<2*m+f[i])
33                 printf("Thrown\n");
34             else printf("0\n");
35             return ;
36         }
37     }
38 }
39 int main(){
40     int n,m,p,t;
41     I(t);
42     while(t--){
43         scanf("%d%d%d",&n,&m,&p);
44         solve(n,m,p);
45     }
46     return 0;
47 }
View Code

 

 

posted @ 2013-09-01 16:37  _随心所欲_  阅读(251)  评论(0编辑  收藏  举报