约瑟夫环问题

本文介绍了如何使用循环链表、数组模拟和队列算法来解决经典的约瑟夫问题,描述了在猴子选举大王的场景中,通过给定的整数序列预测最终获胜者的方法。
摘要由CSDN通过智能技术生成

Description

话说二哥当年学习数据结构的时候遇到了那道猴子报数的题目,其实这就是经典的约瑟夫问题。

可是当年的二哥还是个毛头小子,只会用模拟的方法,而其他同学却使用了一些令二哥完全摸不到头脑的方法。

……二哥一怒之下改了题目……

话说当年花果山的猴子要选大王,选举办法如下:

所有猴子按1-M编号围坐一圈,二哥站在圈中心,由二哥指定一个整数Kn,

之后猴子们从1号开始按顺序报数,报到Kn的猴子退出到圈外,二哥再报出一个整数Kn+1,

然后由刚刚退出的猴子的下一只猴子再开始报数,如此循环报数,直到圈内只剩下一只猴子时,这只猴子就是大王。

由于二哥希望通过此种方法控制花果山,所以现在二哥把他制定的整数序列告诉你,希望你帮他预先算出那只猴子会成为大王。

Input Format

第一行 一个整数M,表示一共有M只猴子

第二行到第M行,每行一个整数 表示二哥即将指定的M-1个整数。这些数都大于0。

Output Format

一个整数,表示最后剩下那只猴子的编号。

Hint

对于40%的数据,M<=1000, K<=1000

对于70%的数据,M<=10000, K<=10000

对于100%的数据,M<=10000, K<=100000000

Sample Input

5
1
2
3
4复制

Sample Output

4复制

时空磁盘限制(运行时)

时间限制:1000 ms

内存空间限制:256 MiB

磁盘空间限制:不可使用磁盘

单个测试点时空限制详情

 

方案一:循环链表(注意移动次数取模,否则会超时)

#include<iostream>
using namespace std;
class List {
private:
	//结点类型
	struct node {
		int data;
		node* next;
		node(int x, node* n = nullptr) :data(x), next(n) {};
	};

	int len;
	node* head;//头结点
	
	
public:
	List(int m = 0);
	~List();
	int Josephus(int m); //约瑟夫游戏函数,每一次返回下个人结点地址
	

};




List::List(int m)
{
	len = m;
	if (m == 0) head = nullptr;
	else {
		head = new node(1);
		node* p = head, * q = nullptr;
		for (int i = 2; i <= m; i++)
		{
			q = new node(i);
			p->next = q;
			p = q;
		}
		p->next = head; //初始化循环链表
	}
}

int List::Josephus(int m)
{
	if (len == 1)  return head->data;
	int k = 0;
	node* p = head, * q;
	for (int i = 0; i < m - 1; i++)
	{
		cin >> k;
		if (k == 1)  k = len + 1;
		for (int i = 0; i < (k - 2)%len; i++)
		{
			p = p->next;
		}
		q = p->next;
		p->next = q->next;
		len--;
		p = p->next;
	}
	head = p;
	return p->data;
}

List::~List()
{
	if (head)
	{
		delete head;
		head = nullptr;
	}

}

int main()
{
	int m = 0;
	cin >> m;
	List mylist(m);
	int n = mylist.Josephus(m);
	cout << n << endl;
	return 0;
}

方案二:数组模拟

#include<iostream>
#define MAX 15000
using namespace std;
int main()
{
	int m = 0;
	cin >> m;
	int len = m;
	int arr[MAX];
	int now = 0;
	for (int i = 0; i < m; i++)
	{
		arr[i] = i;
	}
	int k = 0;
	for (int i = 0; i < m - 1; i++)
	{
		cin >> k;
		int cnt = 1;
		while (cnt <= (k-1)%len)
		{
			now = (now + 1) % m;
			while(arr[now]==-1) now = (now + 1) % m;
			cnt++;
		}
		arr[now] = -1;
		len--;
		now = (now + 1) % m;
		while (arr[now]==-1) now = (now + 1) % m;
	}
	now = (now + 1) % m;
	while (arr[now]==-1) now = (now + 1) % m;
	cout << arr[now]+1 << endl;
	return 0;
}

方案三:队列

#include<iostream>
#include<queue>
using namespace std;
int main()
{
	queue<int> q;
	int m = 0, k;
	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		q.push(i); //入队
	}

	for (int i = 0; i < m-1; i++)
	{
		cin >> k;
		for (int j = 0; j < (k - 1)%len; j++)
		{
			q.push(q.front());
			q.pop();
		}
		q.pop();
	}
	cout << q.front() << endl;
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值