约瑟夫环问题
历史
这个问题是以弗拉维奥.约瑟夫斯命名的,它是1世纪的一名犹太历史学家。他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁。约瑟夫斯和另一个人是最后两个留下的人。约瑟夫斯说服那个人,他们向罗马军队投降,不再自杀。约瑟夫斯把他的存活归因于运气或天意,他不知道是哪一个
约瑟夫环问题
N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。
请按退出顺序输出每个退出人的原序号算法思想
这里介绍3种算法,两种直观,一种运用数学
循环链表
建立一个有N个元素的循环链表,然后从链表头开始遍历并计数,如果基数i == m,则踢出该元素,继续循环,直到当前元素与下一个元素相同时退出循环
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct lnode
{
int pos;
struct lnode *next;
};
/**
* Description:构建循环链表
*/
struct lnode* create_ring(int n)
{
int i;
struct lnode *pre, *pcur, *phead;
phead = (struct lnode *)malloc(sizeof(struct lnode));
phead->pos = 1;
phead->next = NULL;
pre = phead;
for (i = 2; i <= n; i ++) {
pcur = (struct lnode *)malloc(sizeof(struct lnode));
pcur->pos = i;
pre->next = pcur;
pre = pcur;
}
pre->next = phead;
return phead;
}
/**
* Description:约瑟夫环
*/
void kickoff_ring(struct lnode *head, int p, int n)
{
struct lnode *pre, *pcur;
pre = pcur = head;
int i = 1;
while (pcur != NULL) {
if (i == p) {
printf("%d ", pcur->pos);
pre->next = pcur->next;
free(pcur);
pcur = pre->next;
i = 1;
}
pre = pcur;
pcur = pre->next;
if (pre == pcur) {
printf("%d\n", pre->pos);
free(pcur);
break;
}
i ++;
}
}
int main()
{
int p, n;
struct lnode *head;
while (scanf("%d %d", &n, &p) != EOF) {
head = create_ring(n);
kickoff_ring(head, p, n);
}
return 0;
}
数组模拟
思想跟循环链表类似,少了构建循环链表的过程
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i, index, p, n, remain, delete[3001], flag[3001] = {0};
while (scanf("%d %d", &n, &p) != EOF) {
remain = n;
index = 0;
while (remain >= 1) {
for (i = 0; i < n; i ++) {
if (flag[i] == 0) {
// 报数
index ++;
// 报p者退出圈外
if (index == p) {
// 退出圈外
flag[i] = 1;
// 重新报数
index = 0;
delete[remain - 1] = i + 1;
remain --;
}
}
}
}
// 输出每个退出人的序号
for (i = n - 1; i >= 0; i --) {
if (i == 0) {
printf("%d\n", delete[i]);
} else {
printf("%d ", delete[i]);
}
}
}
return 0;
}
缺陷:
用链表和数组实现约瑟夫环,有一个特点:要模拟整个游戏过程,不仅程序写起来比较复杂,而且时间复杂度为O(np),当n和p非常大的时候,几乎没有办法在短时间内得出结果,我在九度用循环链表和数组做约瑟夫环的时候,就超时了
数学推导
暂时不会
链表优化
在runtime无数次之后,我将两个模块合并就ac了,看来我对c里面指针的掌握还是差很多,直接上ac代码吧,原理一样的!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct lnode
{
int pos;
struct lnode *next;
};
/**
* Description:构建循环链表&&循环遍历
*/
void create_ring(int n, int p)
{
int i;
struct lnode *pre, *pcur, *phead;
phead = (struct lnode *)malloc(sizeof(struct lnode));
phead->pos = 1;
phead->next = NULL;
pre = phead;
for (i = 2; i <= n; i ++) {
pcur = (struct lnode *)malloc(sizeof(struct lnode));
pcur->pos = i;
pre->next = pcur;
pre = pcur;
}
pre->next = phead;
pcur = phead;
while (pcur->next != pcur) {
for (i = 1; i < p; i ++) {
pre = pcur;
pcur = pcur->next;
}
printf("%d ", pcur->pos);
pre->next = pcur->next;
free(pcur);
pcur = pre->next;
}
printf("%d\n", pcur->pos);
free(pcur);
}
int main()
{
int p, n;
while (scanf("%d %d", &n, &p) != EOF) {
create_ring(n, p);
}
return 0;
}
/**************************************************************
Problem: 1188
User: wangzhengyi
Language: C
Result: Accepted
Time:40 ms
Memory:912 kb
****************************************************************/

浙公网安备 33010602011771号