数组模拟+公式法求约瑟夫环

def findTheWinner(self, n: int, k: int) -> int:
    idx = 0
    for length in range(2, n + 1):
        idx = (idx + k) % length
    return range(1, n + 1)[idx]
def lastRemaining(self, n: int, m: int) -> int:
    nums = list(range(n))  # 依题意变化1~n或0~n-1
    idx = cnt = 0
    while len(nums) >= 2:
        if cnt == m - 1:
            cnt = 0
            nums.pop(idx)
        else:
            cnt += 1
            idx = (idx + 1) % len(nums)
    return nums[0]
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time : 2023/3/20 16:54
# @Author : Jin Echo
# @File : ring.py

"""
N个人编号1-N,围成一个环,从第一个人开始从1报数,报到M此人退出,
然后从后面一个人继续从1开始报数,当只剩一个人时,输出此人编号
"""

N, M = 5, 3
nums = list(range(1, N + 1))

i, j = 0, 0
while len(nums) > 1:
    if i == M - 1:
        i = 0
        nums.pop(j)
        print(nums)
    else:
        i += 1
        j = (j + 1) % len(nums)

print(nums[0])  # 4

测试:
输入40 1 3
输出31
解释:共40人,编号1-40,从第一人起开始报号,报到3此人出局。则最后出局的人的编号是31

数组模拟:

/**
 *1  2  3  4  5  6  7  8,第四个人,s=4,下标3 
 *0  1  2  3  4  5  6  7

 */
#include<iostream>
using namespace std;

void Josegh(int p[], int n, int s, int m){
	for(int i=0; i<n; i++)
		p[i] = i+1;
		
	int s1 = s-1;				//该轮报号起始下标 
	for(int i=n; i>=2; i--)	{	//当前还活着的人数
		s1 = (s1+m-1)%i;	//当前出圈人的下标
		
		int tmp = p[s1];			//记录出圈人的编号,下面会被覆盖 
	
		int j;
		for(j=s1; j<=i-2; j++) 
			p[j] = p[j+1];
		p[j] = tmp;	
		
		//s1++;				//更新下一轮报号起始下标.错误,后面往前移了,下一轮起始下边就是上一轮删除的下标 
					
	}
}

int main(){
	const int MAXN = 50;
	int p[MAXN];
	int n, s, m;
	cin>>n>>s>>m;
	Josegh(p,n,s,m);
	for(int i=n-1; i>=0; i--)
		cout<<p[i]<<" ";
	return 0;
}

Vector模拟

#include<iostream>
#include<vector>
using namespace std;

//n个人编号1-n, 从第s个人起,报到m退出 
vector<int> Fun(int n, int s, int m){
	vector<int> v;
	vector<int>ans;
	
	for(int i=1; i<=n; i++)
		v.push_back(i);
		
	int s1 = s-1;	//第一轮的报数的起始下标 
	while(!v.empty()){
		s1 = (s1+m-1)%v.size();	//该轮淘汰的下标,也是下轮起始报号下标。因为该轮淘汰后,后面的人往前移,此时上一轮淘汰的下标即为该轮的报数起始下标 
		ans.push_back(v[s1]);
		v.erase(v.begin()+s1); 
	}
	
	return ans;
}

int main(){
	int n, s, m;
	cin>>n>>s>>m;
	vector<int>ans = Fun(n,s,m);
	for(vector<int>::iterator it = ans.begin(); it!=ans.end(); it++)
		cout<<*it<<" ";
	
	return 0;
}
		

**

公式法

**

/**
 *求解约瑟夫环
 *n个人围成一圈,从1开始报数,报到m退出(该情形下等价于第m个人退出),下一个人重新从1开始报数,循环往复直到只剩一人,求最后一人最开始的序号
 *注意:下面求解函数默认n个人的编号为0~n-1(若编号为1-n,只需把结果+1)
 *递推公式:f(n,m) = [f(n-1,m)+m]%n
 *只有一个人的时候, 答案为0、即n=1,f(1,m)=0; 
 */ 
 //1-41,m=3,ans=31(return p+1)
 
#include<iostream>
using namespace std;

int cir(int n,int m)
{
    int p=0;				//n=1时 
    //反推 ,不断的补充m个位置模上当时的数组大小 
    for(int i=2;i<=n;i++) 	//n=2~n 
    {
        p=(p+m)%i;
    }
    return p;				//or p+1
}

int main(){
	int n,m;
	cin>>n>>m;
	cout<<cir(n,m);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值