????海盜分金分配原則:等級最高的海盜提出一種分配方案。所有的海盜投票決定是否接受分配,包括提議人。并且在票數(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;
}
}