고전 알고리즘------Joseph의 문제(C 언어)

 목차

머리말

이야기 배경

조셉 문제

순환 연결 목록 솔루션

 어레이 솔루션


머리말

        오늘 우리는 흥미로운 문제인 요셉 문제를 다룰 것입니다. 이 문제는 중세 유럽의 이야기에서 유래되었습니다. 이제 이 흥미로운 문제를 프로그래밍을 통해 해결해 보겠습니다. 살펴보겠습니다!

이야기 배경

유명한 유대 역사가 요세푸스는 다음과 같은 이야기를         했다고 합니다 : 로마인들이 호타팟을 함락시킨 후, 39명의 유대인들이 요세푸스와 그의 친구들과 함께 동굴에 숨었습니다. 39명의 유대인들은 적에게 잡히느니 차라리 죽기를 결심했습니다. 자살 방법이 결정되었습니다. 41명이 원형으로 줄을 서서 첫 번째 사람이 세기 시작했고, 세 번째 사람이 셀 때마다 그 사람은 자살하고, 다음 사람은 모두가 자살할 때까지 다시 숫자를 세었습니다. 죽을 때까지. 그러나 요세푸스와 그의 친구들은 이를 따르기를 원하지 않았습니다. 먼저 한 사람부터 시작해 k-2명을 건너고(첫 번째 사람이 넘어갔기 때문에), k 번째 사람을 죽인다. 그런 다음 k-1명을 지나서 k 번째 사람을 죽이세요. 이 과정은 마침내 한 사람만 남을 때까지 원을 따라 계속되며, 이 사람은 계속 살 수 있습니다. 문제는 처형을 피하기 위해 처음에 어디에 서야 하는가입니다. 요세푸스는 친구에게 먼저 순종하는 척 해달라고 부탁했고, 친구와 자신을 16번째와 31번째 자리에 배치해 데스게임에서 벗어났다.

        17세기 프랑스 수학자 가스파르(Gaspar)는 <수학 게임>에서 신도 15명과 불신자 15명이 심해에 빠져 위험에 처했고, 그 중 절반은 살아남기 위해 바다에 던져져야 했던 이야기를 했다. 그게 어려워서 30명이 둥글게 모여서 9번째 사람을 모두 바다에 던져버리는 방식을 생각해냈는데, 이런 순환이 15명만 남을 때까지 계속됐다. 바다에 던져질 때마다 불신자들이 되도록 어떻게 마련할 것인지 묻는다.

조셉 문제

키보드에서 n과 s라는 두 개의 데이터를 얻습니다. n은 사람 수를 나타내고, s는 첫 번째 사람부터 세는 것을 나타냅니다. s번째 사람이 세어지면 그 사람이 나옵니다. 질문: 마지막 남은 사람은 누구입니까? 여러 ?

순환 연결 목록 솔루션

        여기서 우리는 순환 연결 리스트 형태로 이 문제를 해결할 수 있으며, 프로세스 다이어그램은 다음과 같습니다.

먼저 현재 입력된 인원 수를 기준으로 해당 원형 연결 리스트를 생성하고, 그 안에 각 사람의 위치를 ​​차례로 저장합니다.

 그러면 아래와 같이 첫 번째 사람부터 카운트를 시작하고 세 번째 사람이 카운트되면 삭제 작업을 수행한 후 네 번째 노드부터 새로운 라운드를 시작하게 되는데...

코드는 다음과 같습니다. 

#include <stdio.h>
#include<stdlib.h>
//节点
typedef struct node {
	int num;
	struct node* next;
}Node;

//创建一个环形链表
Node* create_list(int n) {
	Node* head, * tail;
	head = tail = NULL;
	for (int i = 0; i < n; i++) {
		Node* p = (Node*)malloc(sizeof(Node));
		p->num = i + 1;    //依次标记当前位置
		if (head == NULL) {
			head = p;
			tail = p;
			head->next = NULL;
		}
		else
		{
			tail->next = p;
			tail = p;
		}
		tail->next = head;
	}
	return head;  //返回头结点
}

int main() {
	int n, s;
	printf("请输入:");
	scanf("%d %d", &n, &s);
	Node* cur = create_list(n);
	int count = 1; //此时cur指向的是第一个节点,所以count为1
	while (n != 1) {	//当n=1时候,结束循环,此时剩下最后一个人
		count++;//先进行count统计
		if (count == s) {
			n--;	
			//进行删除节点操作
			Node* del_node = cur->next;	
			cur->next = del_node->next;
			free(del_node);//释放掉这个节点
			//此时count回归到1,也就是重新开始新的一轮
			count = 1;
		}
		cur = cur->next;
	}
	printf("最后剩下的是:%d\n", cur->num);
}

 어레이 솔루션

         연결리스트와 다르게 배열은 인원수를 커스터마이징할 수 없기 때문에 여기에 배열의 수를 미리 적어두어 배열의 실행 효율이 더 높다는 뜻이다.순환 연결리스트는 순회를 통해 실행해야 하며, 시간 복잡도가 더 높은 것은 O(n)이고, 배열은 O(1)의 시간 복잡도로 이 위치를 직접 찾을 수 있으므로 하나씩 탐색할 필요가 없습니다. 코드는 다음과 같습니다.

//数组实现
#include<stdio.h>
void function(int* num, int length, int s, int start) {
	int count = 0;
	int i = start - 1;//标记起始位置
	int n = length;	//当前人数
	while (n != 1) {
		i = (i + s - 1) % n;	//下一个出局人的位置
		for (int j = i + 1; j < length; j++)
			num[j - 1] = num[j];	//进行删除操作,把要删除的数字后面的依次往前移动,覆盖掉这个要删除的数字
		n--;//删除操作完成,减少一个人
		if (i == n) {	//当i超出数组范围的时候,i就回归到第一个为止
			i = 0;
		}
	}
	printf("最后一个 :%d", num[i]);
	return;
}

int main() {
	int num[6];//这里就已经定义好了6个人
	int s;	//每次数到s,出局一个
	printf("请输入:");
	scanf("%d", &s);
	for (int i = 0; i < sizeof(num) / sizeof(int); i++)
		num[i] = i+1;
	function(num, sizeof(num) / sizeof(int), s, 0);
}

오늘은 여기까지입니다. 다음에 만나요!

배경화면 공유:

추천

출처blog.csdn.net/m0_73633088/article/details/133031010