海盗分金-动态规划实现

经济学上有个“海盗分金”模型:是说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]

即兴写的,如果有疏漏请多多包涵

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