约瑟夫问题(洛谷P1996)

约瑟夫问题(洛谷P1996)

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

注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 n−1 名小朋友,而该题是全部出圈。

输入格式

输入两个整数 n,m,1≤m,n≤100。

输出格式

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

输入样例

10 3

输出样例

3 6 9 2 7 1 8 5 10 4

一.用结构体实现单向动态链表

#include<bits/stdc++.h>
using namespace std;
struct node{
	int data;
	node *next;
};
 
int main(){
	int n,m;
	scanf("%d%d",&n,&m); 
	node *head,*p,*now,*prev;
	head=new node;
	head->data=1;
	head->next=NULL;
	//分配节点一,值为1 
	now=head;
	for(int i=2;i<=n;i++){
		p=new node;
		p->data=i;
		p->next=NULL;
		now->next=p;
		now=p; 
 	}
	now->next=head;//最后一个指向第一个 
	//建立链表
	
	now=head,prev=head;
	while((n--)>1){
		for(int i=1;i<m;i++){
			prev=now;
			now=now->next; 
		}
		printf("%d ",now->data);
		prev-> next=now->next;
		delete now;
		now=prev->next;
		//输出并删除此元素 
	}
	printf("%d",now->data);
	delete now; 
	return 0;
}

二.用结构体数组实现单向静态链表

用结构体数组实现单向静态链表,注意静态分配应该定义在全局,不能定义在函数内部.

//用结构体数组实现单向静态链表
#include<bits/stdc++.h>
const int N=105; // 定义静态链表的空间大小
struct node{ // 单向链表
	int id,nextid; //单项指针
    // int date; // 如有必要,定义一个有意义的数据
}nodes[N]; // 定义在全局的静态分配
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	nodes[0].nextid=1;
	for(int i=1;i<=n;i++){
		nodes[i].id=i;
		nodes[i].nextid=i+1;
	}
	nodes[n].nextid=1; // 循环链表:尾指向头
	
	int now=1,prev=1; // 从第1个节点开始
	while((n--)>1){
		for(int i=1;i<m;i++){
			prev=now;
			now=nodes[now].nextid;
		} // 数到m停下
		printf("%d ",nodes[now].id); // 带空格打印
		nodes[prev].nextid=nodes[now].nextid; // 跳过now节点,即删除now
		now=nodes[prev].nextid; // 新的now
	}
	printf("%d",nodes[now].nextid); // 打印最后一个节点,后面不带空格
	return 0;
} 

三.用结构体数组结束双向静态链表

//用结构体数组结束双向静态链表
#include<bits/stdc++.h>
const int N=105;
struct node{ // 双向链表
	int id;// 节点编号
    //int date; // 如有必要,定义一个有意义的数据
    int preid,nextid; // 前一个节点,后一个节点
}nodes[N]; 
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
 
	for(int i=1;i<=n;i++){ // 建立链表
		nodes[i].id=i;
		nodes[i].preid=i-1; // 前节点
		nodes[i].nextid=i+1; // 后节点
	} 
	nodes[n].nextid=1; // 循环链表:尾指向头
	nodes[1].preid=n; // 循环链表:头指向尾
	
	int now=1; // 从第1个节点开始
	while((n--)>1){
		for(int i=1;i<m;i++)
			now=nodes[now].nextid; // 数到m停下
		printf("%d ",nodes[now].id); // 打印,后面带空格
		int prev=nodes[now].preid,next=nodes[now].nextid;
		nodes[prev].nextid=nodes[now].nextid; // 删除now
		nodes[next].preid=nodes[now].preid;
		now=next; // 新的开始
	}
	printf("%d",nodes[now].nextid); // 打印最后一个节点,后面不带空格
	return 0;
} 

四.用一维数组实现单向静态链表

//用一维数组实现单向静态链表 
#include<bits/stdc++.h>
int nodes[150];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n-1;i++)
		nodes[i]=i+1; // nodes[i]的值就是下一个节点
	nodes[n]=1; // 循环链表:尾指向头
	int now=1,prev=1; // 从第1个节点开始
	while((n--)>1){
		for(int i=1;i<m;i++){ // 数到m停下
			prev=now;
			now=nodes[now]; // 下一个节点
		}
		printf("%d ",now); // 带空格
		nodes[prev]=nodes[now]; // 跳过now节点,即删除now
		now=nodes[prev]; // 新的now节点
	} 
	printf("%d",now); // 打印最后一个节点,后面不带空格
	return 0;
} 

五.用c++库中STL的list 

//STL:list
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,m;
	cin>>n>>m;
	list<int>node;
	for(int i=1;i<=n;i++)
		node.push_back(i); // 建立链表
		
	list<int>::iterator it=node.begin();//迭代器,.begin()首元素的指针,即node[0]的指针 
	while(node.size()>1){ // list的大小由STL自己管理
		for(int i=1;i<m;i++){ // 数到m
			it++;
			if(it==node.end())it=node.begin();//.end()末尾的下一个元素的指针,类似于空指针,不指向任何元素 // 循环:到末尾再回头
		}
		cout<<*it<<" ";
		list<int>::iterator next=++it;
		if(next==node.end()) next=node.begin(); // 循环链表
		node.erase(--it); // 删除这个节点,node.size()自动减1
		it=next;
	}
	cout<<*it;
	return 0;
}

六. 一般解法

#include<bits/stdc++.h>
using namespace std;
int n,m,next[1000005];
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
    	next[i]=i+1;
	next[n]=1;
	int p=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<m;j++)
			p=next[p];//
		cout<<next[p]<<" ";
		next[p]=next[next[p]];
	}
	return 0;
}

七.数学解法

总人数s12345678910111213141516171819
存活位置w11313571357911131513357

从中可以看出:当S=2^{n}+x(n,x均为自然数)时,

                         W=2x+n。

大家可以试试用数学来解这道题。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值