【博弈論】 海盜分金問題


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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM