故事:五个海盗抢到了100个金币,每一颗都一样的大小和价值连城。
他们决定这么分:
1.抽签决定自己的号码 ------ [1、2、3、4、5]
2.首先,由1号提出分配方案,然后大家5人进行表决,当且仅当不少于半数(包括半数)的人同意时,按照他的提案进行分配,否则将被扔入大海喂鲨鱼。
3.如果1号死后,再由2号提出分配方案,然后大家4人进行表决,当且仅当不少于半数的人同意时,按照他的提案进行分配,否则将被扔入大海喂鲨鱼。
4.以次类推
条件:每个海盗都是很聪明的人,都能很理智的判断得失,从而做出选择。
他们决定这么分:
1.抽签决定自己的号码 ------ [1、2、3、4、5]
2.首先,由1号提出分配方案,然后大家5人进行表决,当且仅当不少于半数(包括半数)的人同意时,按照他的提案进行分配,否则将被扔入大海喂鲨鱼。
3.如果1号死后,再由2号提出分配方案,然后大家4人进行表决,当且仅当不少于半数的人同意时,按照他的提案进行分配,否则将被扔入大海喂鲨鱼。
4.以次类推
条件:每个海盗都是很聪明的人,都能很理智的判断得失,从而做出选择。
问题:第一个海盗提出怎样的分配方案才能够使自己免于下海以及自己获得最多的金币呢?
初看到这个问题时感觉很难啊,但换个思路就好办了。
1.想要不被喂鱼,那么必须拉拢到半数的人(包括自己在内)
2.第一位海盗拉拢的人的应该是 第二个人提出的最佳方案下收入最少的那一半的人,只要这一半人比第二的方案收入高就Ok
3.第二位海盗的思路重复以上过程
4.倒数第二位海盗没有喂鱼的风险,所有最佳方案是 100 ,0
由此可见以上过程是一个递归的过程,那么我们来写代码吧
public class PirateGold {
public static void main(String[] args) {
PirateGold pirateGold = new PirateGold();
for (int i = 2; i < 10; i++) {
int[] best = pirateGold.bestSegmentation(i, 100);
System.out.print("最好的结果海盗数量" + i + ":{");
for (int j = 0; j < best.length; j++) {
System.out.print(best[j] + ",");
}
System.out.println("}");
}
}
/**
* 必须比自己喂鱼后的下一位的分法下让supportCount也就是一半的海盗获得更多的金币才能不喂鱼
*
* @param totalPirates
* @param totalGolds
* @return
*/
public int[] bestSegmentation(int totalPirates, int totalGolds) {
if (totalPirates <= 2) {
return new int[] { totalGolds, 0 };
}
int[] best = new int[totalPirates];
int supportCount = needHowManyPiratesupport(totalPirates);
// 自己喂鱼后的最佳分法
int[] nextBestSegmentation = bestSegmentation(totalPirates - 1, totalGolds);
// 喂鱼后的最佳分法最穷的海盗,拉拢收益最少的海盗支出最少嘛
int[] poorestPirates = poorestPirate(supportCount, nextBestSegmentation);
// 给自己需要拉拢最少数量的最穷的海盗多一枚金币,其它海盗一分不给,剩下的全给自己就是最好的了
for (int i = 0; i < poorestPirates.length; i++) {
best[poorestPirates[i] + 1] = nextBestSegmentation[poorestPirates[i]] + 1;
totalGolds -= best[poorestPirates[i] + 1];
}
best[0] = totalGolds;
return best;
}
/**
* 需要多少海盗支持才能不喂鱼
*
* @param totalPirates
* @return
*/
private int needHowManyPiratesupport(int totalPirates) {
return (totalPirates - 1) / 2;
}
/**
* 在某种金币分法下的最穷海盗
* 求数组中最小的supportCount个数,不能对原数组排序,因为海盗顺序不能变啊,O(n)复杂度的不好用,nlog(k)复杂度的还行
*
* @param supportCount
* 除自己外需要获得的最少的支持者数量
* @param nextBestSegmentation
* 自己喂鱼后的最佳分法,只能在这个分法下至少让supportCount也就是一半的海盗获得更多的金币才能不喂鱼
* @return 最穷的海盗的下标,不是他有多少金币
*/
private int[] poorestPirate(int supportCount, int[] nextBestSegmentation) {
int[] poorEstPirates = new int[supportCount];
for (int i = 0; i < nextBestSegmentation.length; i++) {
if (i < poorEstPirates.length) {
// 获得最小堆,是下标哦
poorEstPirates[i] = i;
if(i == poorEstPirates.length-1) {
// 排序
sortPoorList(poorEstPirates, nextBestSegmentation);
}
} else {
/**
* 当前海盗比最小堆的最大值小,替换之
*/
if (nextBestSegmentation[i] < nextBestSegmentation[poorEstPirates[0]]) {
poorEstPirates[0] = i;
sortPoorList(poorEstPirates, nextBestSegmentation);
}
}
}
return poorEstPirates;
}
/**
* 最穷海盗排名,冒泡,从大到小
*
* @param poorestPiratesIds
* @param golds
*/
private void sortPoorList(int[] poorestPiratesIds, int[] golds) {
for (int i = 0; i < poorestPiratesIds.length - 1; i++) {
for (int j = 0; j < poorestPiratesIds.length - 1 - i; j++) {
if (golds[poorestPiratesIds[j]] < golds[poorestPiratesIds[j + 1]]) {
int temp = poorestPiratesIds[j];
poorestPiratesIds[j] = poorestPiratesIds[j + 1];
poorestPiratesIds[j + 1] = temp;
}
}
}
}
}
