已经经历过两次考试中都遇到了约瑟夫环问题,就问题本身而言并不难,主要是在理解问题上经常由于题干较短,没有理解清楚意思从而导致无法解题。
问题描述:
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。
历史背景:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。(百度百科)
思路
1.建立链表解决问题
2.通过递归解决问题
建立链表的方法:
1.链表结构
struct Node
{
int data;
Node * next;
};
struct LinkedList
{
Node *pHead;
Node *pTail;
int len;
};
2.建立节点
Node * GetNode(int i)
{
Node* p = (Node*)malloc(sizeof(Node));
if(p!=NULL&&i>=0)
{
p->data = i;
p->next = NULL;
return p;
}
else
{
cout<<"error"<<endl;
exit(-1);
return NULL;
}
3.建立链表
LinkedList* CreateLinkedList(int i)
{
Node* node = GetNode(0);
LinkedList *head = (LinkedList*)malloc(sizeof(LinkedList));
if(head == NULL)
{
cout<<"CreateLinkedList:memory error"<<endl;
exit(-1);
return NULL;
}
if(i<=0)
{
cout<<"CreateLinkedList: error"<<endl;
exit(-1);
return NULL;
}
head->pHead = node;
head->pTail = node;
head->len = 1;
if(i==1)
{
node->next = node;
}
else
{
Node *p = head->pHead;
for(int j=1;j<=i-1;j++)
{
Node* node = GetNode(j);
node->data = j;
p->next = node;上一个节点指向新建的节点
p=p->next;
head->len++;
}
head->pTail = p;
p->next = head->pHead;
}
return head;
}
}
4.删除节点
void RemoveNode(LinkedList*head,Node *deleNode)
{
Node* p = head->pHead;
cout<<deleNode->data<<endl;
if(p!=NULL){
if(head->len>1){
do
{
if(p->data==deleNode->data)
{
if(p==head->pHead)
{
head->pHead = p->next;
}
if(p==head->pTail)
{
head->pTail = p->next;
}
p->data = p->next->data;
p->next = p->next->next;
head->len--;
return;
}else{
p=p->next;
}
}while(p!=head->pHead);
}else
{
cout<<"error";
exit(-1);
return;
}
}else
{
return;
}
}
5.实现约瑟夫环
int Joseph(int m,int k)
{
if(m<=0||k<=0)
{
cout<<"error:input"<<endl;
return -1;
}
LinkedList* list = CreateLinkedList(m);
Node * p = list->pHead;
for(int i=1;i<=k;i++)
{
if(list->len ==2)
{
cout<<p->data<<endl;
cout<<"answer is :"<<endl;
return p->next->data;
}
if(i==k)
{
RemoveNode(list,p);
i = 1;
}
p=p->next;
}
return 0;
}
6.主函数
int main()
{
cout<<"Print output queue"<<endl;
cout<<Joseph(20,3)<<endl;
return 0;
}
7.测试
总代码:
#include<iostream>
#include<malloc.h>
using namespace std;
struct Node
{
int data;
Node * next;
};
struct LinkedList
{
Node *pHead;
Node *pTail;
int len;
};
Node * GetNode(int i)
{
Node* p = (Node*)malloc(sizeof(Node));
if(p!=NULL&&i>=0)
{
p->data = i;
p->next = NULL;
return p;
}
else
{
cout<<"error"<<endl;
exit(-1);
return NULL;
}
}
LinkedList* CreateLinkedList(int i)
{
Node* node = GetNode(0);
LinkedList *head = (LinkedList*)malloc(sizeof(LinkedList));
if(head == NULL)
{
cout<<"CreateLinkedList:memory error"<<endl;
exit(-1);
return NULL;
}
if(i<=0)
{
cout<<"CreateLinkedList: error"<<endl;
exit(-1);
return NULL;
}
head->pHead = node;
head->pTail = node;
head->len = 1;
if(i==1)
{
node->next = node;
}
else
{
Node *p = head->pHead;
for(int j=1;j<=i-1;j++)
{
Node* node = GetNode(j);
node->data = j;
p->next = node;
p=p->next;
head->len++;
}
head->pTail = p;
p->next = head->pHead;
}
return head;
}
void RemoveNode(LinkedList* head,Node *deleNode)
{
Node* p = head->pHead;
cout<<deleNode->data<<endl;
if(p!=NULL){
if(head->len>1){
do
{
if(p->data==deleNode->data)
{
if(p==head->pHead)
{
head->pHead = p->next;
}
if(p==head->pTail)
{
head->pTail = p->next;
}
p->data = p->next->data;
p->next = p->next->next;
head->len--;
return;
}else{
p=p->next;
}
}while(p!=head->pHead);
}else
{
cout<<"error";
exit(-1);
return;
}
}else
{
return;
}
}
int Joseph(int m,int k)
{
if(m<=0||k<=0)
{
cout<<"error:input"<<endl;
return -1;
}
LinkedList* list = CreateLinkedList(m);
Node * p = list->pHead;
for(int i=1;i<=k;i++)
{
if(list->len ==2)
{
cout<<p->data<<endl;
cout<<"answer is :"<<endl;
return p->next->data;
}
if(i==k)
{
RemoveNode(list,p);
i = 1;
}
p=p->next;
}
return 0;
}
int main()
{
cout<<"Print output queue "<<endl;
cout<<Joseph(20,3)<<endl;
return 0;
}
通过递归解决问题
呃呃呃…
推导的递推公式可以理解,但是具体怎么推导出来的还不清楚,的确有时候“怎么想到的“是一个比较玄学也看数学功底的事情,也欢迎知道的将方法说出来,分享知识。下面粘出公式:
Joseph(n,k) = (Joseph(n-1,k)+k) % n (n>1);
#include<iostream>
#include<malloc.h>
using namespace std;
int Joseph(int m,int k)
{
if(m<=0||k<=0)
{
cout<<"error!"<<endl;
return -1;
}else
{
if(m==1)
{
return 0;
}
else
{
return ((Joseph(m-1,k)+k)%m);
}
}
}
int main()
{
cout<<"Print "<<endl;
cout<<Joseph(20,3)<<endl;
system("pause");
return 0;
}
粘出参考的一篇博文,十分感谢博主的总结,我对博主的文章加了部分注释和自己的理解。但是值得指出的是原博主的代码在链表法中存在着问题,例如Joseph(20,3),用迭代法可以得出正确答案为19,但是链表法得出的答案是12,这主要是因为在处理只剩下两个数字的情况时出了错误,显而易见,只剩两个数字时,若喊得是偶数退出,则必定是第二个退出留下第一个,若喊得是奇数退出,则必定是第一个退出留下第二个,我对此处代码进行了改进,总结进了总代码中。
截图
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)