在看《天行九歌》的时候,看到了这个问题,之前在刷OI竞赛题的时候也刷到过一个,今天打算理一下关于这个问题的思路。
首先三姬分金和五海盗分赃其实都是一类问题“纳什均衡”,最初来源于经济学,在分析之前要先明白一些关于博弈论的知识:
- 局中人同时做决策的博弈,叫“静态博弈”。
- 如果决策有先后,后面的人,可以根据前面人的决策,决定和调整自己的决策,就叫“动态博弈”。
- 先决策的称之为:先手优势;
- 最后决策的:低端人权,夹在中间,受制于两端的称其为:夹层;
- 参与博弈的双方,事先都对规则十分清楚,又叫“完全信息”。
我们先看来自战国时期的三姬分金【谷歌后发现实际上中国古代并没有这个问题,是《天行九歌》虚构的,其原型是海盗分赃】:
三姬分金:100两黄金,每姬提出一个分金的方案,如果前一人的方案不被后面的人认同,她就要被处死,以此类推,按照抽签决定甲乙丙先后顺序。你会怎么分?
从表面上来看,第一个人无论提出什么样的要求,第二个人和第三个人都可以否定第一个人的方案,然后被处死,第三个人在否定第二个人的方案,第二个人被处死,最后所有金币都归第三个人所有。
事实上真的这样吗?我们这么来分析:假如第一人自己给自己分99份,然后给第二人1份,从而她不能在分,也不能不认同第一人的方案,因为她如果不认同虽然可以将第一人杀死,但是她接过100份金以后不论自己怎么分都不会得到第三人认同,因为只要第三人不认同,第二人就会死,这样第三人独的100份金。因此,在这种情况下,第二个人必须认同第一个人,得到1个金币,而且还能保命。
这里还有个问题,就是为什么还要给第二个人1个金币,而不是第一个人拿100金币呢,这里其实反映的是人性问题,防止第二个人破罐子摔碎,反正我自己什么都没得到,你还这么难为我,那我拿不到,你也别想活着,然后就不同意第一个人的方案。
所以说,有时候,看似处于劣势,其实处于最优势。 好了,下面继续扩展,把三个人变成五个人,就成了海盗分赃问题了。
五海盗分赃:5个海盗抢得100枚金币,他们按抽签的顺序依次提方案:首先由1号提出分配方案,然后5人表决,超过半数同意方案才被通过,否则他将被扔入大海喂鲨鱼,依此类推。海盗在自己的收益最大化的前提下乐意看到其他海盗被扔入大海喂鲨鱼,假定每个海盗都是绝顶聪明且很理智,那么第一个海盗提出怎样的分配方案才能够使自己的收益最大化?
逆推法,从后向前推,如果1至3号强盗都喂了鲨鱼,只剩4号和5号的话,5号一定投反对票让4号喂鲨鱼,以独吞全部金币。所以,4号惟有支持3号才能保命。
3号知道这一点,就会提出“100,0,0”的分配方案,对4号、5号一毛不拔而将全部金币归为已有,因为他知道4号一无所获但还是会投赞成票,再加上自己一票,他的方案即可通过。
不过,2号推知3号的方案,就会提出“98,0,1,1”的方案,即放弃3号,而给予4号和5号各一枚金币。由于该方案对于4号和5号来说比在3号分配时更为有利,他们将支持他而不希望他出局而由3号来分配。这样,2号将拿走98枚金币。
同样,2号的方案也会被1号所洞悉,1号并将提出(97,0,1,2,0)或(97,0,1,0,2)的方案,即放弃2号,而给3号一枚金币,同时给4号(或5号)2枚金币。由于1号的这一方案对于3号和4号(或5号)来说,相比2号分配时更优,他们将投1号的赞成票,再加上1号自己的票,1号的方案可获通过,97枚金币可轻松落入囊中。这无疑是1号能够获取最大收益的方案了!答案是:1号强盗分给3号1枚金币,分给4号或5号强盗2枚,自己独得97枚。
分配方案可写成(97,0,1,2,0)或(97,0,1,0,2)
下面再来看看HDU上的题目:http://acm.hdu.edu.cn/showproblem.php?pid=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.
代码实现:
#include#include using namespace std; int t; 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)//j { if(n!=p and (n%2==p%2)) { cout<<"1\n";return ; } if(p==n) { cout< 1)/2<<endl; return ; } else { cout<<"0"<<endl; return ; } } if(n==2*m+1) { if(p<2*m and p&1) { cout<<"1\n";return ; } else { cout<<"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() { cin>>t; while(t--) { int n,m,p; scanf("%d%d%d",&n,&m,&p); slove(n,m,p); } return 0; }