海盜分金算法實(shí)現(xiàn)

????海盜分金分配原則:等級最高的海盜提出一種分配方案。所有的海盜投票決定是否接受分配,包括提議人。并且在票數(shù)相同的情況下,提議人有決定權(quán)如果提議通過,那么海盜們按照提議分配金幣。如果沒有通過,那么提議人將被扔出船外,然后由下一個(gè)最高職位的海盜提出新的分配方案。wiki
????假設(shè)每一個(gè)海盜都很機(jī)智,先利己再損人,按優(yōu)先級遵守如下3條原則:
????????1. 自己要能活下來。
????????2. 拿更多的銀子.哦!不,是金子!
????????3. 好想看排在前面的海盜被丟到海里。

????算法實(shí)現(xiàn)思路:首先根據(jù)策略算出除自己外還需要多少票,接著算出可以用金幣收買多少海盜,然后算出為了保命而投票的海盜數(shù),如果兩者相加都不夠,只能被丟到海里。問題的難點(diǎn)在于隨著人數(shù)的增多,為了保命而投票的海盜數(shù)會動(dòng)態(tài)變化。大家可以思考一下,如果有50個(gè)海盜分配5個(gè)金幣,按照規(guī)則,有哪些海盜必死無疑,而哪些海盜能夠幸運(yùn)地活下來。

????以下是java算法實(shí)現(xiàn):

package com.basic.T;
import java.util.Collections;
import java.util.LinkedList;

public class PiratePuzzle {
    
    
    public static void main(String[] args) {
            int totalPirates = 10;
            int totalGold = 2;
            double strategy = 1/2d;//半數(shù)或半數(shù)以上
            for (int i =1; i < totalPirates ; i++) {
                allocation(i,totalGold,strategy);
            }
            
    }
    
    /**
     * @param restPirates 剩余海盜數(shù)量
     * @param totalGold 總金額
     * @param strategy 策略
     */
    public static void allocation(int restPirates,int totalGold,double strategy){
        if(restPirates <1){
            System.out.println("世界上只有10種人,一種是懂二進(jìn)制的,另一種是測試");
        }else if(restPirates==1){
            System.out.println("無敵是多么寂寞,所以他的分配方案為("+totalGold+")");
        }else if(restPirates > 1){
            //海盜分金屬于逆向推導(dǎo)過程,初始情況設(shè)為只剩1個(gè)海盜(totalGold)的情況下往回推導(dǎo)
            int i=1;
            LinkedList<Integer> link = new LinkedList<Integer>();
            link.add(totalGold);
            //100%純保命型投票選手
            int voteForLife= 0;
            //占投票的坑,取最近的voteForLife的個(gè)數(shù)
            int voteForLifeCopy= 0;

            //可否逃避喂鯊魚的命運(yùn)?
            boolean destinedDied = false;
            while(restPirates>i){
                i++;
                //根據(jù)策略,除自己外還需要多少票**如果要不包含策略,例如strategy為1/2時(shí),但是必須要半數(shù)以上才行,那么如下i改為(i+1)**
                int leftAgree = new Double(Math.ceil(i*strategy)-1).intValue();
                //利用金幣最多可以收買的海盜數(shù)目
                int voteForGoldMax = getVoteForGoldMax(link,totalGold);
                //如果票數(shù)達(dá)不到,則可能有生命危險(xiǎn)
                boolean surrendDeath = voteForLife + voteForGoldMax< leftAgree ;
                //這個(gè)voteForLifeCopy存在有點(diǎn)繞,舉個(gè)例子說明一下,假設(shè)4個(gè)人分2個(gè)金幣,且**半數(shù)以上**投票才能通過,
                //假設(shè)從左往右分配,那結(jié)果為(0,0,1,1) (2,0,0) GG (2) 
                //只有死亡線上的海盜(們)可以占坑,并且這些坑只能且只能為緊跟他們左側(cè)的海盜所用,所以坑位只能取上一次的結(jié)果,
                //voteForLifeCopy就是為了保存上一次的值.繞過死亡線后,坑位數(shù)重置
                voteForLifeCopy = voteForLife;
                if(surrendDeath){
                    voteForLife++;
                    voteForLifeCopy=voteForLife;
                    //你排著隊(duì),拿著死神的號碼牌
                    if(restPirates==i){
                        destinedDied = true;
                    }
                }else{
                    voteForLife=0;
                }
                if(!destinedDied){
                    link = reAllocation(surrendDeath,link,leftAgree-voteForLifeCopy,totalGold);
                }
            }
            printResult(destinedDied,restPirates,totalGold,link);
        }
    }

    private static LinkedList<Integer> reAllocation(boolean surrendDeath,
            LinkedList<Integer> link,int voteForGold,int totalGold) {
        LinkedList<Integer> linkCopy = new LinkedList<Integer>(link);
        //分配結(jié)果中轉(zhuǎn)站
        LinkedList<Integer> linkCopy2 = new LinkedList<Integer>(link);
        int givenSum = 0;
        if(!surrendDeath){
            Collections.sort(link);
            for (int j = 0; j < linkCopy2.size(); j++) {
                linkCopy2.set(j, 0);
            }
            for (int j = 0; j < voteForGold ; j++) {
                Integer min=link.removeFirst();
                int index = linkCopy.lastIndexOf(min);
                linkCopy.set(index,totalGold+1);//重新設(shè)值是為了改變上一步lastIndexOf的結(jié)果
                linkCopy2.set(index, min+1);//收買的方式就是多給一塊錢
                givenSum=givenSum+min+1;//這么多人要收買,我快破產(chǎn)了,民主制度好坑啊
            }
            linkCopy2.addFirst(totalGold-givenSum);
        }else{
            //小命保住就好,要什么自行車?
            linkCopy2.addFirst(0);
        }
        return linkCopy2;
    }

    public static void printResult(boolean destinedDied, int restPirates,
            int totalGold,LinkedList<Integer> link) {
        if(destinedDied){
            //System.out.println("一家招聘公司的人事經(jīng)理隨手抓起桌子上的簡歷,并告訴他們被錄取了。有時(shí)候,運(yùn)氣才是核心競爭力!");
        }else{
            StringBuffer sb =new StringBuffer();
            sb.append(restPirates+"個(gè)海盜分配"+totalGold+"個(gè)金幣的方案是(");
            for (Integer integer : link) {
                sb.append(integer).append(",");
            }
            sb.replace(sb.lastIndexOf(","), sb.lastIndexOf(",")+1, ")");
            System.out.println(sb);
        }
    }

    public static int getVoteForGoldMax(LinkedList<Integer> link, int totalGold) {
        LinkedList<Integer> sort = new LinkedList<Integer>(link);
        Collections.sort(sort);
        int i=0;
        while(sort.size()>0 &&(totalGold=totalGold-(sort.removeFirst()+1))>-1){
            i++;
        }
        return i;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 博弈還真是有點(diǎn)意思 最近在看一本有關(guān)博弈的書,其中有幾個(gè)例子比較有意思,展列在此,供大家消遣,前半部分是問題,后半...
    EnjoyTheLife閱讀 758評論 0 1
  • 現(xiàn)實(shí)問題的最優(yōu)解一定繞不開人性,心理反應(yīng)一定在理性決策考慮之中 題目描述如下: “5名超級聰明、求生欲和殺人欲很強(qiáng)...
    月曦月夕閱讀 1,657評論 1 3
  • 5個(gè)海盜搶得100枚金幣后,討論如何進(jìn)行公正分配。他們商定的分配原則是:(1)抽簽確定各人的分配順序號碼(1,2,...
    博格體閱讀 2,213評論 0 1
  • 下午他爸爸接的孩子,等我下班回來,他爸爸說孩子的數(shù)學(xué)做完了,還剩語文沒有做了,正在做呢,大兒子今天表現(xiàn)很棒,...
    152e93d48a17閱讀 193評論 0 0
  • UTC時(shí)間2017年8月1號,在比特幣區(qū)塊高度478558時(shí), 比特幣網(wǎng)絡(luò)出現(xiàn)了"分叉",一個(gè)叫Bitcoin C...
    蘇江同學(xué)閱讀 2,458評論 3 6