海盗分金问题
?
題目描述
有5個海盜1、2、3、4、5,得到100個金幣,決定分掉,分法怪異:首先A提出分法,B~E表決,如果不過半數同意,就砍掉A的頭。然后由B來分,C~E表決,如果不過半數同意,就砍掉B的頭。依次類推,如果假設強盜都足夠聰明,在不被砍掉頭的同時獲得最多金幣。問:最后結果如何?
?
定義問題
5個海盜必須按照上述規則,找出最優分配方案,否則將被其他人扔下大海。
?
當前狀態
正確分配方案還沒出來,必須盡快找出最優解。
?
分析
典型的nim取子問題的變形,采用倒推方式即可找出最優解。
?
制定解決方案
倒推方法如下所示。
(1)如果只有海盜,如何分配?
(2)如果有兩個海盜,如何分配?
(3)如果有n個海盜,每個海盜至少需要幾個人同意?找出n-1個海盜情況下的分配方案中,需要收買多少海盜?最低需要多少金幣?
?
實現解決方案
倒推方法實現如下表所示,每一行表示一張人數情況下的分配方案,即,最后一行給出5號海盜的分配方案。
注意:從表中可以看出聰明是大前提,當然殘忍也是。nim取子游戲向來是君子游戲,沒有這個前提的弧,后面的海盜不按套路出牌就不可能有最優解。
?
標準化解決方案
從上表不難看出規律,也即,自己拿盡可能多的金幣,同時保證后面的人依次分配0、1交錯的金幣數。規范化命題如下:
*命題:假設對于n個聰明、殘忍的海盜(n>=1),搶了Gold個金幣,要進行分配,每個人都想分配足夠多的金幣,他們寸步不讓。分配順序是:n號海盜>n-1號海盜....1號海盜,即是:先由n號海盜決定如何分配,若分配不均,n號被殺死,n-1號海盜再決定如何分配,依次類推。在該規則下,n號海盜最佳分配方案應當如此(為簡單,避免使用地板函數,按n為奇偶數分類討論):
不妨假設ones為得到1個金幣好海盜,zeros為得到0個金幣的海盜數。
(1)當n為奇數時,ones = zeros = (n - 1) / 2,分配方案為Gold-ones,0,1,.......,0,1
(2)當n為偶數時,ones = (n - 2) / 2, zeros = n / 2,分配方案為Gold - ones,0,1,......1,0
該命題從上述分析中的規律中得出,不是定理,更不是公理,作為標準化解決方案需要嚴格證明。
證明:
注釋:使用跳躍數學歸納法證明,跳躍的step為2。
先證明n為奇數時命題是否成立。
當n = 1時,顯然該命題成立
假設當n = m(m > 1)時命題成立,則分配方案為
Ones(m) = Zeros(m) = ( m - 1) / 2
Proposa(m) = Gold - Ones(m),0,1,......,0,1
= Gold - (m - 1) / 2, 0,1.....,0,1
當n = m + 1時,顯然命題成立,理由如下
Ones(m + 1) = (m + 1 - 2) / 2 = (m?- 1) / 2,Zeros(m + 1) = (m + 1) / 2
注意:
(a)m+1為偶數,計算ones和zeros需要換個公式
(b)對比n=m情形,僅僅是多了個0
Proposal(m + 1) = Gold - Ones(m + 1), 0, 1,.....,0, 1, 0
= Gold - (m - 1)/2,0, 1, ......., 0, 1, 0
對比Ones(m), Ones(m + 1), Zeros(m), Zero(m + 1)可以看出僅僅是在Proposal(m)序列后面添個0。這回導致Proposal(m + 1)與Proposal(m)兩個序列中,0,1交錯序列剛好錯位,對應金幣分配方案中則為:m+1號海盜分配方案就是把Proposal(m)中沒有得到金幣的人每個給1個金幣實現自己金幣最大化,同時保證同意的人數大于等于2.
綜上所述,當n為奇數時,該命題成立。
再證明n為偶數時命題是否成立。
當n = 2,命題成立(大于的最小偶數只能2)
歸納方法同1.
綜合1、2分析,該命題成立。
證畢。
?
該標準化解決方案用程序實現如下
說明:該程序考慮了金幣數量遠小于海盜數量時,最先出來分配的部分海盜都被殺死了。
1 #include <stdio.h> 2 3 const int g_first = 2; 4 const int g_zero = 0; 5 const int g_one = 1; 6 7 int print_proposal(int *gold, int *pirate, int *ones, int *zeros, int *status) 8 { 9 int ret = 1; 10 int number = 0; 11 int remain = *gold - *ones; 12 if (*pirate <= 0) 13 { 14 printf(".\n"); 15 return 0; 16 } 17 if (remain <= 0) 18 { 19 number = 0; /* not enough gold, killed by others, regenerate proposal */ 20 ret = 1; /* conitnue */ 21 22 /* regenerate proposal */ 23 if (*pirate % 2 == 0) 24 { 25 *ones = (*pirate - 2) / 2; 26 *zeros = *pirate / 2; 27 } 28 else 29 { 30 *ones = *zeros = (*pirate - 1) / 2; 31 } 32 } 33 else if (*status == g_one) 34 { 35 number = 1; 36 *ones--; 37 *status = g_zero; 38 } 39 else if (*status == g_zero) 40 { 41 number = 0; 42 *zeros--; 43 *status = g_one; 44 } 45 else { 46 number = remain; 47 *status = g_zero; 48 } 49 if (*status != g_first && *ones <= 0 && *zeros <= 0) 50 { 51 ret = 0; 52 } 53 54 printf("%d ", number); 55 *pirate = *pirate - 1; 56 57 return ret; 58 } 59 60 int main() 61 { 62 int gold; 63 int pirate; 64 int ones; 65 int zeros; 66 int temp; 67 int status; /* process first, zero or one */ 68 printf("Please Enter gold and pirate number:\n"); 69 while (scanf("%d %d", &gold, &pirate) == 2) 70 { 71 if (pirate < 1 || gold < 1) 72 { 73 break; 74 } 75 if (pirate % 2 == 0) 76 { 77 ones = (pirate - 2) / 2; 78 zeros = pirate / 2; 79 } 80 else 81 { 82 ones = zeros = (pirate - 1) / 2; 83 } 84 85 status = g_first; 86 printf("Proposal(%d, %d) is: ", gold, pirate); 87 temp = pirate; 88 while (print_proposal(&gold, &temp, &ones, &zeros, &status)) 89 ; 90 91 if(pirate == 1) 92 { 93 printf(".\n"); 94 } 95 96 printf("\n"); 97 printf("Please Enter gold and pirate number:\n"); 98 } 99 return 0; 100 }
決定下一步
本問題到此為止,暫時沒有下一步動作。
?
總結
這個結論告訴我們:
(1)最先掌握分配權的人,看似最危險,膽小的人可能會擔心所有人都不支持你,你就只能被殺死了。其實,他優先掌握了主動權,天下武功,唯快不破,掌握市場先機很重要。
(2)越快越好,但不能快過了頭。100個金幣,1000個海盜的話,最先出來分配的人必然死掉。同樣的,20年前搞酒店搜索,率先占據了主動權,快得一塌糊涂那也沒戲。
?
?
?
?
?
該題目源自:微博陳利人。
?
這是菜鳥我見這個問題分析的很透徹的一個帖子,感謝原作者。
?
?
解答引自:http://blog.losthit.com/archives/pirate-and-gold/
轉載于:https://www.cnblogs.com/iloveyouforever/p/3449621.html
總結
- 上一篇: 云时代的风云变换
- 下一篇: 2020下半年新机最新消息_三星小米华为