HDU 1538 A Puzzle for Pirates(经典好题,海盗分金问题)

                     A Puzzle for Pirate   HDU - 1538  

A bunch of pirates have gotten their hands on a hoard of gold pieces and wish to divide the loot. They are democratic pirates in their own way, and it is their custom to make such divisions in the following manner: The fiercest pirate makes a proposal about the division, and everybody votes on it, including the proposer. If 50 percent or more are in favor, the proposal passes and is implemented forthwith. Otherwise the proposer is thrown overboard, and the procedure is repeated with the next fiercest pirate. 
All the pirates enjoy throwing one of their fellows overboard, but if given a choice they prefer cold, hard cash, the more the better. They dislike being thrown overboard themselves. All pirates are rational and know that the other pirates are also rational. Moreover, no two pirates are equally fierce, so there is a precise pecking order — and it is known to them all. The gold pieces are indivisible, and arrangements to share pieces are not permitted, because no pirate trusts his fellows to stick to such an arrangement. It's every man for himself. Another thing about pirates is that they are realistic. They believe 'a bird in the hand is worth two in the bush' which means they prefer something that is certain than take a risk to get more, where they might lose everything. 

For convenience, number the pirates in order of meekness, so that the least fierce is number 1, the next least fierce number 2 and so on. The fiercest pirate thus gets the biggest number, and proposals proceed in the order from the biggest to the least. 

The secret to analyzing all such games of strategy is to work backward from the end. The place to start is the point at which the game gets down to just two pirates, P1 and P2. Then add in pirate P3, P4, ... , one by one. The illustration shows the results when 3, 4 or 5 pirates try to divide 100 pieces of gold. 

 

Your task is to predict how many gold pieces a given pirate will get.
Input
The input consists of a line specifying the number of testcases, followed by one line per case with 3 integer numbers n, m, p. n (1 ≤ n ≤ 10^4) is the number of pirates. m (1 ≤ m ≤ 10^7) is the number of gold pieces. p (1 ≤ p ≤ n) indicates a pirate where p = n indicates the fiercest one. 
Output
The output for each case consists of a single integer which is the minimal number of gold pieces pirate p can get. For example, if pirate p can get 0 or 1 gold pieces, output '0'. If pirate p will be thrown overboard, output 'Thrown'. 
Sample Input
3
3 100 2
4 100 2
5 100 5
Sample Output
0
1
98

        
  
The situation gets complicated when a few gold pieces were divided among many pirates. 
Hint
Hint

 这是一道很强大的题,有很多种要讨论的情况,看了大神的博客后我觉得我想一天也想不出来。



转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

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

http://acm.hdu.edu.cn/showproblem.php?pid=1538

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

同时分为两个状态稳定状态和不稳定状态:如果当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便是本题的解,
代码如下:
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define C    240
#define TIME 10
#define inf 1<<25
#define LL long long
using namespace std;
//保存2的幂
int fac[15]={2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
void slove(int n,int m,int p){
    //金币够贿赂的情况
    if(n<=2*m){
        //不是决策者,而且奇偶性相同,都能被贿赂
        if(n!=p&&(n%2==p%2))
            printf("1\n");
        //剩下的都是决策者拥有
        else if(n==p)
            printf("%d\n",m-(n-1)/2);
        else
        //其余人分不到金币,他们的决策不影响全局
            printf("0\n");
        return ;
    }
    //这时候的不同在于决策者不能拿金币
    else if(n==2*m+1){
        if(p<2*m&&p&1)
            printf("1\n");
        else
            printf("0\n");
        return ;
    }
    int t=n-2*m,i;
    //这是刚好保命的情况,对于决策者来说,肯定没有金币
    //对于其它人来说,要么肯定没有金币,要么可能没有金币,不确定性
    for( i=0;i<14;i++){
        if(t==fac[i]){
            printf("0\n");
            return;
        }
    }
    for( i=1;i<14;i++)
        if(t<fac[i]){
            //决策者必死
            if(p>2*m+fac[i-1]&&p<2*m+fac[i])
                 printf("Thrown\n");
            else
                 printf("0\n");
            return ;
        }
}
int main(){
    int t,n,m,p;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&p);
        slove(n,m,p);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值