约瑟夫环问题

历史

这个问题是以弗拉维奥.约瑟夫斯命名的,它是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
****************************************************************/



posted @ 2013-05-14 20:46  java程序员填空  阅读(455)  评论(0编辑  收藏  举报