约瑟夫问题(洛谷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;
}
七.数学解法
总人数s | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | |
存活位置w | 1 | 1 | 3 | 1 | 3 | 5 | 7 | 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 13 | 3 | 5 | 7 |
从中可以看出:当S=+x(n,x均为自然数)时,
W=2x+n。
大家可以试试用数学来解这道题。