海盜分金-動態規劃實現

經濟學上有個“海盜分金”模型:是說5個海盜搶得100枚金幣,他們按抽籤的順序依次提方案:首先由1號提出分配方案,然後5人表決,投票要超過半數同意方案才被通過,否則他將被扔入大海喂鯊魚,依此類推。

假定“每個海盜都是絕頂聰明且很理智”,那麼“第一個海盜提出怎樣的分配方案才能夠使自己的收益最大化?”

推理過程是這樣的

1.如果只剩兩個海盜 那麼海盜4無論提什麼,海盜5都會否決.

2.所以海盜3 可以提出 100 ,0,0的方案(這裏假設海盜1,2如果入水了)

3.海盜2 要拉攏兩個人才能通過方案,只要放棄3號給4,5兩人1人1個金幣就好了(98,0,1,1)

4.海盜1 也要拉攏人,那麼需要在海盜2的基礎上加碼,放棄海盜2(97,0,1,2,0)

那如果6個海盜呢,7個海盜呢

 以下是算法思路

 1.海盜n要爭取除自己外n/2的人同意

 2.分配按保守來(海盜n,拉攏n/2個人,並且提出的分配方案比他之前的更加有誘惑力 )

  比如 海盜n-1到海盜1提出的方案裏,能給他拉攏的人最多x個金幣,海盜n就給他x+1個

 這個典型的就是動態規劃問題

3.dp去記錄每個海盜的分配方案中 除分配人以外每個人能獲得的最大值

比如4個海盜的時候的dp=[0,1,1](第一個0代表第三個人被放棄了,按照推理每個海盜都會放棄他前一個人)

那麼5個海盜就從中取出5/2=2個最小的值,分別給他們都加1個金幣 (5選擇拉攏成本較小的4,和3)結果就是 dp=[0,1,2,1]

6個就是[0,1,2,2,2](注意dp是前面方案裏的最大值,所以把海盜n要拉攏的對象+1,其他保持不變)

以下是代碼實現

public class Coin {
    //n>3
    public List<List<Integer>> solution(int n,int coin) {
        List<List<Integer>> results=new ArrayList<>();
        List<Integer> ret=new ArrayList<>(4);
        Collections.addAll(ret,98,0,1,1);
        results.add(ret);
        List<Integer> dp=new LinkedList<>();
        Collections.addAll(dp,0,1,1);
        for (int i = 5; i <= n; i++) {

            List<Integer> retNow=new ArrayList<>(i);
            //初始化
            for (int i1 = 0; i1 < i; i1++) {
                retNow.add(0);
            }
            //除自己外需要支持的海盜數
            int num = i / 2;
            //找出需要金幣最小的幾個海盜

            Set<Integer> set=new HashSet<>();
            int sum = 0;
            while (--num>=0){
                int min=Integer.MAX_VALUE;
                int index=0;
                for (int j = 0; j < dp.size(); j++) {
                    if(min>dp.get(j)&&!set.contains(j)){
                        min=dp.get(j);
                        index=j;
                    }
                }
                set.add(index);
                min=min+1;
                sum+= min;
                dp.set(index,min);
                retNow.set(index+2,min);
            }
            dp.add(0,0);
            if(coin-sum<0)
                break;
            retNow.set(0,coin-sum);
            results.add(retNow);
        }

        return results;
    }

    public static void main(String[] args) {
      List<List<Integer>> list=new Coin().solution(100,100);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
分配結果如下
[98, 0, 1, 1]
[97, 0, 1, 2, 0]
[95, 0, 1, 2, 0, 2]
[94, 0, 1, 2, 3, 0, 0]
[91, 0, 1, 2, 3, 0, 3, 0]
[91, 0, 1, 2, 3, 0, 0, 0, 3]
[86, 0, 1, 2, 3, 4, 4, 0, 0, 0]
[86, 0, 1, 2, 3, 4, 0, 0, 4, 0, 0]
[82, 0, 1, 2, 3, 4, 0, 0, 0, 0, 4, 4]
[80, 0, 1, 2, 3, 4, 5, 5, 0, 0, 0, 0, 0]
[75, 0, 1, 2, 3, 4, 5, 0, 0, 5, 5, 0, 0, 0]
[75, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 5, 5, 0]
[68, 0, 1, 2, 3, 4, 5, 6, 6, 0, 0, 0, 0, 0, 0, 5]
[67, 0, 1, 2, 3, 4, 5, 6, 0, 0, 6, 6, 0, 0, 0, 0, 0]
[61, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0]
[60, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6]
[51, 0, 1, 2, 3, 4, 5, 6, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0]
[51, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0]
[44, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7]
[40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0]
[32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 0, 0]
[21, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8]
[19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0]
[9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9]

即興寫的,如果有疏漏請多多包涵

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章