这是我的备赛用具,主要是用来熟悉链表的。
约瑟夫问题也是经典的C++问题,解法非常多,如果有机会的话,会再出更合集。
老规矩先上题目再上代码。
题目:洛谷P1996
题目描述
n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 11 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 �−1n−1 名小朋友,而该题是全部出圈。
输入格式
输入两个整数 n,m。
输出格式
输出一行 n 个整数,按顺序输出每个出圈人的编号。
输入输出样例
输入 #1复制
10 3
输出 #1复制
3 6 9 2 7 1 8 5 10 4
说明/提示
1≤m,n≤100
题目看着挺简单,实际上也还行。
上代码。
第一位选手是教科书式写法:动态链表,但是太烦,虽然省空间,但是竞赛里一般不用。
//动态链表解法
#include<bits/stdc++.h>
using namespace std;
struct node{//链表节点
int data;//节点的值
node *next;//单向链表只有一个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;
//分配第一个节点,值为一,指向空
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){
n--;
for(int i=1;i<m;i++){//数到m停止
prev = now;//记录上一个位置
now = now -> next;
}
printf("%d ",now->data);//记得输出空格,跳过m
prev->next = now->next;//prev指向now指向的地方,即m
delete now;//释放now
now = prev->next;//now到prev后面去
}
printf("%d\n",now->data);//最后输出
delete now;//释放节点
return 0;
}
第二位选手是结构体数组单向静态链表。
//结构体数组单向静态链表解法
#include<bits/stdc++.h>
using namespace std;
const int N = 105;//静态链表的空间大小
struct node{
int id,nextid;//当前编号,下一个的编号
}nodes[N];
int main(){
int n,m;
cin>>n>>m;//输入
nodes[0].nextid = 1;//0的下一个是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;
while((n--) > 1){
for(int i=1;i<m;i++){
prev = now;//保存prev
now = nodes[now].nextid;//now后移
}
cout<<nodes[now].id<<' ';//输出
nodes[prev].nextid = nodes[now].nextid;//删除now,建立链接
now = nodes[prev].nextid;//now重建
}
cout<<nodes[now].nextid;//最后一个人
return 0;
}
第三位选手是结构体数组双向静态链表。
//结构体数组双向静态列表解法
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
struct node{
int id,preid,nextid;//当前,前,后
}nodes[N];
int main(){
int n,m;
cin>>n>>m;
nodes[0].nextid = 1;//这行代码是增加辨识度的(跟前面差不多)
for(int i=1;i<=n;i++){
nodes[i].id = i;//id
nodes[i].preid = i - 1;//前节点
nodes[i].nextid = i + 1;//后节点
}
nodes[n].nextid = 1;//n的后面一个是1
nodes[1].preid = n;//1的前面一个是n
int now = 1;
while((n--) > 1){
for(int i=1;i<m;i++){
now = nodes[now].nextid;
}//now后移,跳m
cout<<now<<' ';//输出
//下面三行有点绕,但是忍一忍,懒了没用变量
nodes[nodes[now].preid].nextid = nodes[now].nextid;
//now前节点与now后节点建立链接
nodes[nodes[now].nextid].preid = nodes[now].preid;
//now后节点与now前节点建立链接
now = nodes[now].nextid;//now后移
//相当于把now释放之后重新建立
}
cout<<nodes[now].nextid;//输出最后一个被淘汰的人的编号
return 0;
}
最后是最简单但是使用环境最有限的一维数组单向静态链表解法。
//普通一维数组单向静态链表解法,里面最简单的,但是使用环境有限
//i就是当前节点的值,nodes[i]是下一个节点
#include<bits/stdc++.h>
using namespace std;
const int N = 105;//常量
int nodes[N];//节点数组
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<n;i++) nodes[i] = i + 1;//初始化
nodes[n] = 1;//首尾相连
int now = 1,prev = 1;
while((n--) > 1){
for(int i=1;i<m;i++){
prev = now;
now = nodes[now];//老规矩,prev先保存,now后移
}
cout<<now<<' ';
nodes[prev] = nodes[now];//建立链接
now = nodes[prev];//now重建
}
cout<<now;//最后一个被淘汰的人的编号
return 0;
}
结束。
再见。
本文内容基本来自于《算法竞赛 上册》