推广的海盗分金币问题的算法
来源:互联网 发布:linux怎么启动jenkins 编辑:程序博客网 时间:2024/04/29 04:21
在杨军blog上看见一个海盗分金币问题,很有趣,准备在这里推广并用计算机可操作的算法实现。原题的描述以及解题思路杨军已经给出了,详见hi.baidu.com/yjpro/blog/category/%D6%C7%C1%A6%D1%B5%C1%B7。
推广:
n个海盗,m个金币。其中n >= 2,m >= 1。
基本思想:
杨军已经给出,关键就是“倒着想”,并假设每个海盗都很理智。每个海盗都有一个金币数量的收入的期望,如果他必死,那么他的收入期望是-1。如果第k个海盗来制定分配方案(因为是倒着想,所以不考虑前k-1个海盗),那么他需要贿赂他后面n-k个海盗中不少于一半的海盗,使得他们给自己投赞成票。为了使自己的收入最多,他贿赂的方式就是从期望收入最少的海盗开始,在他原来的期望收入基础上再加1个金币,直到贿赂了足够多的海盗。这样描述不准确,但是大体思想说出来了。
算法:
利用数学归纳法。初始化时,将海盗顺序标号为1~n。将第n个海盗的期望收入置为m,第n-1个海盗的期望收入置为-1。假设后k个海盗的收入期望已经算出来,那么倒数第k+1个海盗的收入期望这样计算:将后k个海盗按收入期望升序排序,选择前(k+1)/2个期望,每个期望加1,用m减去这些期望的和,得出的就是倒数第k+1个海盗的收入期望。如果该期望小于0(证明总金币量不够他贿赂足够的人,他将死去,因为我们假设海盗是一群糙人,他们不愿意看见决策者没有给自己比期望更多的贿赂:)),那么置该海盗的收入期望为-1;否则,该海盗贿赂成功,被选出的(k+1)/2个海盗每个人的期望收入都增加1,其余k-(k+1)/2个海盗的期望收入变为0。继续迭代。
程序伪码:
数据结构<id, e>,其中id表示海盗的id值,e表示其期望收入。
L:线性表,最好为链表,存储海盗节点<id, e>。L中第一个元素是L[1]。
Insert(L, <id, e>):L上的操作,将<id, e>插入到L“适当的”位置。何为“适当”?稍后解释。
Sort(L):将L中的海盗节点按照e值升序排序。
main:
initial:
Insert(L, <n, m>);
Insert(L, <n-1, -1>);
begin:
for i = [3 to n] //倒着来
e = 0;
for k = [1 to i/2]
e += (L[k].e + 1);
endfor;
if e > m //不够贿赂,这个海盗死定了!
e = m+1;
else
for k = [1 to i/2] //开始分赃了
L[k].e++;
for k = [i/2+1 to n] //这些海盗因为倒数第i个海盗的存在而期望归0
L[k].e = 0;
endif;
Insert(L, <n-i+1, m-e>); //将倒数第i个海盗入列
Sort(L);
endfor
return i, L(i).e = max(L(j).e, j = [1 to n] );
end
解释:
这里引入Sort操作,完全是为了叙述方便而进行的抽象。因为,由于算法本身的特点,如果每次更新L中相关节点的e值后,将e值为0的节点cut并粘贴到 L的前面,那么形成的新L是升序排列的。再选择适当的位置将新海盗插入到L中。所以,每次更新后用sort排序,如果n很大,将是一种很大的时间开销。
- 推广的海盗分金币问题的算法
- 海盗分金币的问题
- 海盗分金币的问题
- 海盗分金币问题
- 海盗分金币问题
- 海盗分金币问题
- 海盗分金币问题
- 海盗分金币问题 【转载】
- 经典数学逻辑问题--海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 海盗分金币
- 动态FormBean实现文件上传(转)
- 处女日志
- 软件测试工程师的进阶要求
- velocity用法简单实例说明
- sip简介
- 推广的海盗分金币问题的算法
- 在vs2008/vs2005中对C++项目做单元测试(Unit Test)
- load() Function for PHP - Fetch URL Content
- 移动MM商城,不可重蹈梦网覆辙
- 莫为SOA而SOA
- SQLServer中的Scanf和Printf
- 修复ubuntu启动项之重写Grub和MBR
- 我的第一个FFT核,成功了
- 性能强劲的Tokyo Cabinet 和 Tokyo Tyrant