//***********************************
// Author: jianquan
// Email: livespring#at#foxmail#dot#com
// Time: 2014-09-29 19:12:19
// Copyright (C) 2014 jianquan. All Rights Reserved.
//
// 海盗分配钻石问题:m个海盗,n颗钻石
// 经济学上有个“海盗分钻石”模型,是说5个海盗抢得100颗钻石,他们按抽签的顺序依次提方案:
// 首先由1号提出分配方案,然后5人表决,超过半数同意方案才被通过,否则他将被扔入大海喂鲨鱼,依此类推
//
// 假设前提:
// 1 假定“每人海盗都是绝顶聪明且很理智”
// 2 海盗们都很贪婪,在保命的条件下会想要更多的钻石
// 3 在分析能获得同样多钻石的情况下,他们会选择投反对票
//
// 那么,最后的分配结果是什么?
//
//***********************************
#include <iostream>
#include <map>
#include <set>
using namespace std;
const int MaxValue=INT_MAX;
typedef multimap<int,int>::iterator multiMapItor;
typedef multimap<int,int> multiMapInt;
typedef map<int,int> mapInt;
bool updateSolution(multiMapInt &rank,mapInt &solution,int person,int personNum,int gemNum)
{
//+1保证大于一半,-1因为自己肯定同意
int agree=(personNum-person+1)/2+1-1;
//试探是否可以分配下来,否则不更新原结果
mapInt tmpSolution=solution;
multiMapItor itrMap;
// multimap遍历比普通map麻烦:用set取出所有的key
set<int> setKey;
for (itrMap = rank.begin(); itrMap != rank.end(); itrMap++) {
setKey.insert(itrMap->first);
}
//争取到规定人数前,每人原有利益+1(所以尽量选原有利益少的),人数够了后不分配钻石
set<int>::iterator pIterKeySet;
int personCnt=1,gemCnt=0;
pair<multiMapItor,multiMapItor> PairIter;
for (pIterKeySet = setKey.begin(); pIterKeySet != setKey.end(); pIterKeySet++) {
int iCurrentKey = *pIterKeySet;
//返回迭代器的pair(确定相同key的区间)
PairIter = rank.equal_range(iCurrentKey);
while (PairIter.first != PairIter.second) {
if(personCnt<=agree) {
int NUM = PairIter.first->first+1;
gemCnt=gemCnt+NUM;
//取>=: 海盗是自私的,哪怕分配者给自己分配0也会被杀
// 原因:有一个海盗会得到2颗,杀掉会改变得2颗的位置
if(gemCnt>=gemNum)
return false;
//钻石不够分配了,此次分配取消,分配角色被杀
tmpSolution[PairIter.first->second]=NUM;
} else
tmpSolution[PairIter.first->second]=0;
personCnt++;
PairIter.first++;//必要步骤,遍历相同key的不同value,
//如果要考虑选择的随机性而不是顺序选择,可以加代码实现
}
}
tmpSolution[person]=gemNum-gemCnt;
solution=tmpSolution;
return true;
}
//当gems>=(pirateNum/2+1+1)时不会有人死掉:
//前一个+1保证多于一半人同意;后面+1因为有一个人会分到2颗
void pirateAllocateGems(int pirateNum,int gemsNum)
{
multiMapInt rank; //分得的宝石数-海盗编号
mapInt solution; //海盗编号-分得的宝石数
//初始化,假如只有最后两个海盗分的方案:(0,gemsNum)
for(int i=1; i<pirateNum-1; ++i)
solution[i]=MaxValue;//赋为很大的数方便判断是否会被杀掉
solution[pirateNum-1]=0;
solution[pirateNum]=gemsNum;
// 保障自己不死并能分配最多宝石的方法是:让更多人愿意接受这个方案而不是杀死自己后重新的方案
// 从只剩下3个海盗开始反推分配方案
for(int p=pirateNum-2; p>0; p--) {
rank.clear();//更新rank
//将solution的value通过map升序排列
for(int i=p+1; i<=pirateNum; ++i)
rank.insert(make_pair(solution[i],i));
if(!updateSolution(rank,solution,p,pirateNum,gemsNum))
break;//钻石不够,已经没有合理分配了,前面的海盗必死
}
//打印结果
cout<<pirateNum<<" pirates to allocate "<<gemsNum<<" gems:"<<endl;
for(int i=1; i<=pirateNum; i++)
if(MaxValue==solution[i])
cout<<" pirate: "<<i<<"\ttoo few gems, must be killed by others "<<endl;
else
cout<<" pirate: "<<i<<"\tgems: "<<solution[i]<<endl;
cout<<endl;
cin.get();
}
void main()
{
cout<<"********* pirateAllocateGems ********"<<endl;
while(1) {
cout<<"please input the number of pirate and gem: ";
int m,n;
cin>>m>>n;
if(m>=2)
pirateAllocateGems(m,n);
else
break;
}
}
海盗分宝石的求解和扩展
最新推荐文章于 2024-11-16 21:36:47 发布