HDU 1538 A Puzzle for Pirates
這是一個經典問題,有n個海盜,分m塊金子,其中他們會按一定的順序提出自己的分配方案,如果50%或以上的人贊成,則方案通過,開始分金子,如果不通過,則把提出方案的扔到海里,下一個人繼續。現在給出n,問第k個海盜(第n個海盜先提方案,第1個最后提方案)可以分到多少金子,還是會被扔到海里去。
首先我們講一下海盜分金決策的三個標准:保命,拿更多的金子,殺人,優先級是遞減的。
同時分為兩個狀態穩定狀態和不穩定狀態:如果當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號。
如此類推下去,如果n<=2*m時候,前面與n相同奇偶性的得到1個金子,剩下的第n個人全部拿下。
但是會有一個問題便是,如果金子不夠賄賂怎么辦:
我們將問題具體化:如果有500個海盜,只有100個金子,那么前面200個已經分析過了。
對於201號來說,拿出100個金子賄賂前面的第200號分金子時拿不到金子的100個人。自己不拿金子,這樣剛好有101票保證自己不死,如果分給之前能拿到金子的人,那么之前拿不到金子的人反正無論如何也拿不到金子,不如把你殺了。
對於202號來說,自己不能拿金幣,而賄賂上一輪沒有拿到金幣的101人中的100人就夠了,這樣湊齊101票。
對於203號來說,需要102個人的支持,顯然加上他自己,還需要101票,而金子不夠賄賂,別人會反對,而達到殺人的目的。所以這時其他人會分到多少金子是未知的,只知道203號會被扔進海里,也就是說現在是“不穩定的”狀態,會退化到202號來分100個金子的狀態,那么其他人得到的金子就遵循202號的方案。
對於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^i的時候Pn能保命,否則在第一個2 * m + 2^i之前的海盜都會死亡
但是當n大於 2 * m + 1時,活下來的人分到的金子是不確定的,以202為例:到第202海盜的時候,除了自己剛好需要100個人支持,如果按照前面的當然給2 -- 200中偶數號海盜,但是假如他給201號海盜的話,他也會支持,因為假如202號丟進海里,自己分的話他是得不到的,所以他能拿到金子,當然也支持202號,所以這樣就有101個人只要得到金子就一定會支持,所以雖然存活與否是能夠確定的,但是分得金子是不確定的,要么是0要么是1,但是不過本題中要求輸出海盜可以獲得的最少金子數量,也就是說此時輸出0即可。
所以歸納一下:
當n<=2 * m時:
與n奇偶性相同的獲得1,不同的獲得0,然后剩下的全部由n號海盜獲得
當n==2 * m+1時
第n個人為0,奇數號的人為1,偶數號的人為0
當n>2 * m+1時
設位置滿足2 * m + 2 ^ i的最大的位置為k,大於k的全部扔到海里,其他人獲得金子是不確定的,輸出0
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, a[30];
int main() {
cin >> t;
for (int i = 0; i <= 20; i++) a[i] = pow(2, i);
while (t--) {
int n, m, k;
cin >> n >> m >> k;
if (n <= 2 * m) {
if (k == n)
cout << m - (n - 1) / 2 << endl;
else {
if (k % 2 == n % 2)
cout << 1 << endl;
else
cout << 0 << endl;
}
}
else if(n==2*m+1){
if (k % 2 == 1&&k!=n) cout << 1 << endl;
else
cout << 0 << endl;
}
else {
int flag = 0, pos = 0;
for (int i = 0; i <= 20; i++) {
if (2 * m + a[i] <= n) {
pos = i;
}
}
if (k > 2 * m + a[pos]) {
cout << "Thrown" << endl;
}
else{
cout << 0 << endl;
}
}
}
return 0;
}