海盗分金分配原则:等级最高的海盗提出一种分配方案。所有的海盗投票决定是否接受分配,包括提议人。并且在票数相同的情况下,提议人有决定权如果提议通过,那么海盗们按照提议分配金币。如果没有通过,那么提议人将被扔出船外,然后由下一个最高职位的海盗提出新的分配方案。wiki
假设每一个海盗都很机智,先利己再损人,按优先级遵守如下3条原则:
1. 自己要能活下来。
2. 拿更多的银子.哦!不,是金子!
3. 好想看排在前面的海盗被丢到海里。
算法实现思路:首先根据策略算出除自己外还需要多少票,接着算出可以用金币收买多少海盗,然后算出为了保命而投票的海盗数,如果两者相加都不够,只能被丢到海里。问题的难点在于随着人数的增多,为了保命而投票的海盗数会动态变化。大家可以思考一下,如果有50个海盗分配5个金币,按照规则,有哪些海盗必死无疑,而哪些海盗能够幸运地活下来。
以下是java算法实现:
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;//半数或半数以上
for (int i =1; i < totalPirates ; i++) {
allocation(i,totalGold,strategy);
}
}
/**
* @param restPirates 剩余海盗数量
* @param totalGold 总金额
* @param strategy 策略
*/
public static void allocation(int restPirates,int totalGold,double strategy){
if(restPirates <1){
System.out.println("世界上只有10种人,一种是懂二进制的,另一种是测试");
}else if(restPirates==1){
System.out.println("无敌是多么寂寞,所以他的分配方案为("+totalGold+")");
}else if(restPirates > 1){
//海盗分金属于逆向推导过程,初始情况设为只剩1个海盗(totalGold)的情况下往回推导
int i=1;
LinkedList<Integer> link = new LinkedList<Integer>();
link.add(totalGold);
//100%纯保命型投票选手
int voteForLife= 0;
//占投票的坑,取最近的voteForLife的个数
int voteForLifeCopy= 0;
//可否逃避喂鲨鱼的命运?
boolean destinedDied = false;
while(restPirates>i){
i++;
//根据策略,除自己外还需要多少票**如果要不包含策略,例如strategy为1/2时,但是必须要半数以上才行,那么如下i改为(i+1)**
int leftAgree = new Double(Math.ceil(i*strategy)-1).intValue();
//利用金币最多可以收买的海盗数目
int voteForGoldMax = getVoteForGoldMax(link,totalGold);
//如果票数达不到,则可能有生命危险
boolean surrendDeath = voteForLife + voteForGoldMax< leftAgree ;
//这个voteForLifeCopy存在有点绕,举个例子说明一下,假设4个人分2个金币,且**半数以上**投票才能通过,
//假设从左往右分配,那结果为(0,0,1,1) (2,0,0) GG (2)
//只有死亡线上的海盗(们)可以占坑,并且这些坑只能且只能为紧跟他们左侧的海盗所用,所以坑位只能取上一次的结果,
//voteForLifeCopy就是为了保存上一次的值.绕过死亡线后,坑位数重置
voteForLifeCopy = voteForLife;
if(surrendDeath){
voteForLife++;
voteForLifeCopy=voteForLife;
//你排着队,拿着死神的号码牌
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);
//分配结果中转站
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);//重新设值是为了改变上一步lastIndexOf的结果
linkCopy2.set(index, min+1);//收买的方式就是多给一块钱
givenSum=givenSum+min+1;//这么多人要收买,我快破产了,民主制度好坑啊
}
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("一家招聘公司的人事经理随手抓起桌子上的简历,并告诉他们被录取了。有时候,运气才是核心竞争力!");
}else{
StringBuffer sb =new StringBuffer();
sb.append(restPirates+"个海盗分配"+totalGold+"个金币的方案是(");
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;
}
}