愚人节与自己开了个很大的玩笑,几天没写程序,今天继续!Josephus问题是说N个人围成一个圈传热土豆,先约定一个数M,当传递了M次的时候拿着土豆的人出局,然后将土豆给出局人的下一个人,游戏继续,直到最后只剩下一个人,求出局人的序列(按出局顺序排列)。
这个问题可以用数组实现,但是需要标记代表出局人的元素,并且没遍历一个元素就要检查该元素是否已被标记为出局,这样程序运行时间必然会变慢。另一种方式是使用一个链表,每次把出局的节点删除掉。这样的解决方案非常直观,只需要关注链表中的节点,因为在链表中的节点就是没有出局的。删除节点只是指针的调整,非常迅速,但是释放删除的节点占用的空间会比较费时,当输入N很大时会占用大量的时间。所以采用一种链表与数组结合的方式,可以克服两种方式的缺点。
结合的方式就是把链表放在一个数组中!初始时,每个元素的next指针指向数组的下一个元素,最后一个指向第一个(我们需要的是一个循环链表)。这样我们就可以实现在数组中跳跃性的访问而不必关心访问的节点是否是被标记为出局,最后只要一次性释放掉数组所占用的空间就好了,不用管链表节点的释放!
#include "stdafx.h"
#include <iostream>
using namespace std;
struct person
{
int id;
person* next;
};
struct person_list
{
int count;
person* first;
person* last;
};
person_list* init(int n)
{
person* p = new person[n];
for (int i = 0; i < n - 1; ++i)
{
p[i].id = i + 1;
p[i].next = &p[i+1];
}
p[n - 1].id = n;
p[n - 1].next = &p[0];
person_list* persons = new person_list;
persons->count = n;
persons->first = p;
persons->last = &p[n - 1];
return persons;
}
int* solve(int n,int m)
{
if(n < 1 || m < 0) return NULL;
person_list* persons = init(n);
person* first = persons->first;
person* t = first;
int count = persons->count;
int *losers = new int[n];
int j = 0;
while(count > 1)
{
person* q = NULL;
for (int i = 0; i < m; ++i)
{
q = first;
first = first->next;
}
if(first == persons->first)
{
persons->first = first->next;
persons->last = persons->first;
}
else if(first == persons->last)
{
q->next = first->next;
persons->last = q;
}
else
{
q->next = first->next;
}
--count;
losers[j++] = first->id;
first = first->next;
}
losers[j] = persons->first->id;
delete []t;
delete persons;
return losers;
}
int _tmain(int argc, _TCHAR* argv[])
{
int n = 5, m = 1;
int* losers = solve(n,m);
for (int i = 0; i < n; ++i)
{
cout<<losers[i]<<‘ ‘;
}
cout<<endl;
delete[]losers;
return 0;
}
程序中losers数组的最后一个元素保存的是最后胜利的人,之前才是出局人序列。
生活中许多事情也像写程序,哪里不会出点bug呢,但是bug总会被修复的不是吗?
原文地址:http://blog.csdn.net/liao_jian/article/details/44870993