约瑟夫环问题

题目:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常,我们会要求输出最后一位出列的人的序号。那么这里主要研究的是最后一个出列的人的序号要怎么确定。

首先,数组写成以0开头更便于计算,所以后面的分析都是以0开头的。

原理(缺)

https://blog.csdn.net/byn12345/article/details/79487253

https://www.cnblogs.com/cmmdc/p/7216726.html

 

 

1,借用模(取余),数组可以轻松表示环形。数组 [0, 1, 2, 3, 4, 5] 可以写成:[6%6, 7%6, 8%6, 3%6, 4%6, 5%6]. 我们把递增的起点从第0个元素移动到第3个元素处。即实现了3,4,5,0,1,2的单调递增。

2,把数组 [0,1,2,3,4,5,6,7,8,9] 滚动成 [4,5,6,7,8,9,0,1,2,3]的方法: 相当于是重新把6作为基准,其他数仍然照序递增(包括跨过数组右界回到数组左界,这个过程也是递增的),所以变化方法只需要给出6如何变成0即可: (k-6)%10 , 为了保证括号里大于零,所以额外加个模,即:(k-6+10)%10 ====> (k+4)%10.

 

解法一:用递归公式

每删除一个元素,就把新队列的下标映射成规范形式(即从0开始),这样已知最后一个取得元素其变换后的下标必然是m,则能逆向计算出变换前的下标。

int lastNum(int n, int m) {
	int index = 0, len = 1; //len表示变换前的数组长度
	while (++len <= n) 
		index = (index + m % len + 1) % len;
	return index;
}

解法二:用队列模拟

用队列表示环形数组,并且模拟数数的过程。数一个数相当于从队首取出一个元素,若计数没到k,就把它放回队尾,若到了k,就不再放回(相当于删除掉了),并且重置计数。直到队列剩余1个元素。

#include<queue>
int lastNum(int n,int m) {
	queue<int> q;
	for (int i = 0; i<n; i++)  q.push(i);	//初始队列0~n-1
	int count = 0;
	while (q.size() != 1) {	
		if (count != m) {	//未数到m		
			int tmp = q.front();
			q.pop();
			q.push(tmp);
			count++;
		}
		else {		//数到m		
			q.pop();
			count = 0;
		}
	}
	int res = q.front();
	return res;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值