说明 有人站在一个等待被处决的圈子里。计数从圆圈中的某个点开始,并以固定方向围绕圆圈前进。在每个步骤中,跳过一定数量的人并执行下一个人。消除围绕着圆圈(随着执行的人被移除而变得越来越小),直到只留下最后一个人,给予自由。
我用Google搜索了这个'约瑟夫问题'并且维基百科命中了一个动态编程解决方案:f(n,k)=((f(n-1,k)+k-1) mod n)+1, with f(1,k)=1
,但这只会产生最后的幸存者。如何获得执行人员的顺序?比如,p(5,3)= {3,1,5,2,4}。
此外,是否有O(nlogn)
解决方案而不是O(nk)
解决方案?
答案 0 :(得分:7)
要获得已执行人员和最后幸存者的序列,您只需从头开始模拟整个过程。鉴于程序的描述,这将是非常容易的任务。您提供的公式只是检查谁将存活并快速获得答案的捷径。
如何使用范围树在O(n log n)中执行此操作的说明如下: http://pl.scribd.com/doc/3567390/Rank-Trees
更详细的分析可以在这里找到: http://www.imt.ro/romjist/Volum12/Number12_1/pdf/02-MCosulschi.pdf
答案 1 :(得分:2)
表示人的最自然的数据结构是循环缓冲区。我的解决方案创建一个链表,将列表的尾部重新绑定到头部,然后在缓冲区周围重复计数到下一个要执行的人,从缓冲区中删除该人,并继续直到缓冲区的尾部指向自身
(define (cycle xs)
(set-cdr! (last-pair xs) xs) xs)
(define (josephus n m)
(let loop ((k (- m 1)) (alive (cycle (range 0 n))) (dead '()))
(cond ((= (car alive) (cadr alive))
(reverse (cons (car alive) dead)))
((= k 1)
(let ((dead (cons (cadr alive) dead)))
(set-cdr! alive (cddr alive))
(loop (- m 1) (cdr alive) dead)))
例如:
> (josephus 41 3)
(2 5 8 11 14 17 20 23 26 29 32 35 38 0 4 9 13 18 22 27 31 36
40 6 12 19 25 33 39 7 16 28 37 10 24 1 21 3 34 15 30)
您可以在my blog阅读更全面的解释,它提供了三种不同的解决方案。或者您可以在http://programmingpraxis.codepad.org/RMwrace2运行该程序。
答案 2 :(得分:1)
人们将被存储在大小为n的数组中。如果索引i
的人现在被执行,则下一个将由(i+k)%m
给出,其中m是剩余人数。在每次迭代之后,数组大小将减少1,其余元素将相应地移位。
输入:人物[0..n-1],n,k,i(=第一人执行指数)
伪代码类似于:
Print People[i]
While (n > 1)
do
n = n - 1
for j = i to n-1
People[j] = People[j+1]
i = (i+k) % n
print People[i]
done
答案 3 :(得分:1)
为了激发程序,您可以使用包含玩家名称的结构和一个标记,如果玩家处于活动状态,该标记可以保留跟踪。每次在新一轮中你跳过特定数量的玩家,所以使用循环和条件语句,以便忽略游戏中的所有玩家,并且只计算游戏中的玩家。当然还要添加printf语句来打印当前状态。
答案 4 :(得分:0)
要回答输出执行顺序的问题,需要进行模拟。这意味着O(nk)的复杂性。在同时寻求O(nlogn)时间复杂度的同时,不可能获得执行序列[O(n)]。因为你必须输出每个要执行的人,即O(n)。
public class CircularGunmenIterator<T> implements Iterator<T> {
private List<T> list;
private Iterator<T> iter;
public CircularGunmenIterator(List<T> list) {
this.list = list;
this.iter = list.iterator();
}
@Override
public boolean hasNext() {
// Continue as long as there is a shooter and a victim
return this.list.size() >= 2;
}
@Override
public T next() {
if (!this.iter.hasNext()) {
// Wrap around, creating the illusion of a circular buffer
this.iter = this.list.iterator();
}
return this.iter.next();
}
@Override
public void remove() {
this.iter.remove();
}
public static void main(String[] args) {
// Create the gunmen
List<Integer> gunmen = new LinkedList<Integer>();
for (int i = 1; i <= 100; i++) {
gunmen.add(i);
}
// Shootout!
Iterator<Integer> ringIter = new CircularGunmenIterator<Integer>(gunmen);
while (ringIter.hasNext()) {
Integer shooter = ringIter.next();
Integer victim = ringIter.next();
System.out.printf("%2d shoots %2d\n", shooter, victim);
ringIter.remove(); // Bang!
}
System.out.println("Last one alive: " + gunmen.get(0));
}
}
维基百科上有关于约瑟夫斯问题的更多细节(k = 2)。