练习
3.6 JosePhus 问题(Josephus problem)是下面的游戏:N个人编号1~N,围坐成一个圆圈。从1号人开始传递一个热土豆。经过M次传递后拿着热土豆的人被清除离座,围坐的圆圈锁紧,由坐在后面的人拿起热土豆继续进行游戏。最后剩下的人获胜。因此,如果M=0和N=5,则游戏人依序被清除,5号游戏人获胜。如果M=1 和N=5,那么被清除的人的顺序是2,4,1,5.
a. 编写一个程序解决在M和N为一般的值下的Josephus 问题,应使所编程序尽可能地高效率,要确保各个单元都能被清除。
b. 这个程序的运行时间是多少?
c. 如果M=1,这个程序的运行时间又是多少? 对于N的一些大值(N>100000), delete 例程如何影响该程序的速度?
解答: 执行1000次,需要1.26 秒。
#include <iostream>
#include <chrono> // 求运行时间的库
using namespace std;
using namespace chrono;
typedef struct LNode
{
int data;
struct LNode *link;
LNode()
{
data = 0;
link = NULL;
}
}LNode,*LinkList;
/*
* n 为总人数
* k 为第一个开始传递的人
* m 为拿到土豆被剔除的人
*/
int Josephus(int n, int k, int m)
{
// pnode 为当前节点,prenode 为辅助节点,指向pnode 的前驱节点,curr 为头结点
if(n < 0 || k < 0 || m < 0)
return -1;
LinkList pnode, prenode, curr;
pnode = new LNode();
pnode->data = 1;
pnode->link = pnode; //建立循环链表
curr = pnode;
for (int i = 2; i <= n; i++) {
LinkList temp = new LNode();
temp->data = i;
temp->link = curr->link;
curr->link = temp;
curr = temp;
}
prenode = curr;
while (k--) {
prenode = pnode; //prenode 是pnode的前一个节点
pnode = pnode->link; //移动节点pnode 到第k个元素开始
}
while (n>1)
{
// for 是移动m个位置,pnode会移动到m的位置,prenode 是pnode 的前一个节点
for (int s = m; s>0;--s)
{
prenode = pnode;
pnode = pnode->link;
}
prenode->link = pnode->link; //删除pnode节点
cout << "deleted node is " << pnode->data << endl;
free(pnode); //释放空间
pnode = prenode->link; // 从prenode 的下一个节点开始,数一个m个节点的值
n--;
}
return pnode->data;
}
int main()
{
//k=0,表示从第一个人开始,m表示每次移动m个人
// josePhus(5, 0, 1);
int winner;
auto start = system_clock::now();
for(size_t i=0;i<1000;++i)
winner=Josephus(5,0,1);
auto end = system_clock::now();
auto duration = duration_cast<microseconds>(end - start);
cout << " Consume: " << double(duration.count())*microseconds::period::num / microseconds::period::den << " s(秒)" << endl;
cout <<"The winner is "<< winner << endl;
return 0;
}