约瑟夫问题

目录

题目描述

输入格式

输出格式

样例 #1

样例输入 #1

样例输出 #1

提示

【AC代码】

【用数组模拟】

【用循环链表】

【用递归】

【用队列】


题目描述

n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

输入格式

输入两个整数 n,m。

输出格式

输出一行 n 个整数,按顺序输出每个出圈人的编号。

样例 #1

样例输入 #1

10 3

样例输出 #1

3 6 9 2 7 1 8 5 10 4

提示

1≤m,n≤1000

【AC代码】

【用数组模拟】

#include<bits/stdc++.h>
using namespace std;
int a[110];
//i既代表数组的下标,也代表每个人的编号
//k是用来计数的,一旦k的值达到m,代表此人需要出局,并且k需要重新计数,这样才能够找出所有需要出局的人
//数组的0代表未出局的人,数组非0代表出局的人,未出局的人需要报数,出局的人不需要报数 
int main()
{
	int N,M;
	int cnt=0,i=0,k=0;  //cnt表示目前出局的人数 
	cin>>N>>M;  //表示总共有n人,数到数字m时出局 
	while(cnt!=N) //因为要求N个人的出局顺序,因此当cnt(用来统计已经出局的人)未达到n时,需要循环不断报数 
	{
		i++;   //i是每个人的编号 
		if(i>N) i=1;  //这里需要特别注意:i的值是不断累加的,一旦发现i的值>N,那么i需要重新从第1个人开始
		              //数组要从第一个元素重新开始一个一个往后判断 
		if(a[i]==0)   //只有元素值为0的人 才需要报数,元素值为非0的代表已经出局了,不用报数 
		{
			k++;
			if(k==M)     //代表已经某个人已经报了M这个数,需要出局 
			{
				a[i]=1;  //编号为i的这个人出局 
				cnt++;   //出局的人数+1 
				cout<<i<<" ";  //输出出局的人的编号 
				k=0;   //清空k,让下一个人重新从1开始报数   
			}
		}
	}
	return 0;
} 

【用循环链表】

#include<bits/stdc++.h>
using namespace std;
typedef struct node  //typedef用来重命名struct node这种数据类型,将其命名为Node 
{
	int data;
	struct node* next;
}Node;
 
void ysflb(int N,int M)  //总共有N个人,报到数字为M的人出局 
{
	//初始化循环链表
	Node *head = NULL,*p=NULL,*r=NULL;   //head为头指针,指向链表的第一个结点,一开始赋值为NULL,代表不指向任何结点 
	head = (Node*)malloc(sizeof(Node));  //让head指向一个实际的空间
	if(NULL==head)  //内存空间可能会申请失败,大多数情况不会申请失败 
	{
			cout<<"Memory Failed!";
			return;
	} 
	head->data=1;       //从1开始编号 
	head->next=NULL;    //一开始整个链表只有一个Node(结点),这个Node有两个域,分别是data和next
	                    //data从1开始,next指向NULL,总共需要N个结点,现在创建了一个,还需要N-1个 
    p=head;             //head要保持不能改变,才能够找到链表的起始位置,一开始p也指向第一个结点
	                    //p等一下会被使用,用它可以便于创建剩下的N-1个结点 
						 
	//尾插法创建链表,已经有一个1号结点了,还需要创建剩下的n-1个结点 
	for(int i=2;i<=N;i++)
	{
		r=(Node*)malloc(sizeof(Node)); 
		r->data=i;
		r->next=NULL;
		//插入结点 
		p->next=r;
		p=r;
		
	}
	//创建循环链表
	p->next=head;   //最后一个结点的next指向头结点
	p=head;         //为后续方便,将p指向头结点
	
	//约瑟夫环的模拟
	while(p->next!= p)  //如果p的next=p,说明目前只有一个元素 
	{
		for(int i=1;i<M;i++)  //报到数字为M的时候出局 
		{
			  r=p;   //保留出局的前一个结点 
			  p=p->next; //p指向的是要出局的这个结点,需要保留前一个结点
		}
		// 输出
		cout<<p->data<<" ";
		r->next=p->next;    //删除p的目的,此时p指向哪里?  :  
		p=p->next;  //更新p重新进行报数 
	} 
	cout<<p->data; 
}
 
int main()
{
    int n,m;
	ysflb(n,m);
	return 0;
} 

【用递归】

#include<bits/stdc++.h>
using namespace std;
int ysfdg(int N,int M,int i)
{
    if(i==1)
    {
    	return (M-1+N)%N;
	}
    return (ysfdg(N-1,M,i-1)+M)%N;
}
 
 
int main()
{
	int N,M;
	cin>>N>>M;
	for(int i=1;i<=N;i++)
		cout<<ysfdg(N,M,i)<<" ";
	return 0;
} 

【用队列】

#include<bits/stdc++.h>
using namespace std;
int main(){
    queue<int> q;
    int n,m,j=1;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        q.push(i);
    }
    while(!q.empty()){
        if(j==m){
            cout<<q.front()<<" ";
            q.pop();
            j=1;
        }else{
            j++;
            q.push(q.front());
            q.pop();
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值