海盗分宝石的求解和扩展

//***********************************
// 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;
  }
}

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值