当前位置: 首页 > news >正文

洛谷 P1996 约瑟夫问题之题解

文章目录

    • 题目链接
    • 洛谷P1996 约瑟夫问题 题面
      • 题目描述
      • 输入格式
      • 输出格式
      • 样例1
        • 输入
        • 输出 #1
      • 说明 / 提示
    • 注意:本题多解!
    • 解法 1:STL list
    • 解法 2:动态链表
    • 解法 3:静态链表
      • 1. 用结构体数组实现静态链表
      • 2. 用结构体数组实现双向静态链表
      • 3. 用一维数组实现单向静态链表
    • 附:一组洛谷的评测数据
      • 输入
      • 输出

题目链接

https://www.luogu.com.cn/problem/P1996

洛谷P1996 约瑟夫问题 题面

题目描述

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

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

输入格式

输入两个整数 n,mn,mn,m

输出格式

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

样例1

输入
10 3
输出 #1
3 6 9 2 7 1 8 5 10 4

说明 / 提示

1≤m,n≤1001 \le m, n \le 1001m,n100

注意:本题多解!

我们讲三个解法:

  1. STL list
  2. 动态链表
  3. 静态链表

解法 1:STL list

在算法竞赛或工程项目中常常使用 C++ 中的 STL list。list 是双向链表,由标准模板库(Standard Template Library, STL)管理,通过指针访问节点数据,高效率地插入和删除。

以下代码用 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();while (node.size() > 1) {                           // list 的大小由 STL 自己管理for (int i = 1; i < m; i++) {                   // 数到 mit++;if (it == node.end()) it = node.begin();    // 循环:到末尾后再回头}cout << *it << " ";list<int>::iterator next = ++it;if (next == node.end()) next = node.begin();   // 循环链表node.erase(--it);                               // 删除节点,node.size() 自动减 1it = next;}cout << *it;return 0;
}

解法 2:动态链表

动态链表需要临时分配链表节点,使用完毕后释放链表节点。动态链表的优点是能及时释放空间,不是用多余内存;缺点是需要管理空间,容易出错。动态链表是“教科书式”的标准做法。

以下代码用动态单向链表实现了这道题。

// 动态链表
#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;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;prev = now, now = 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;
}

解法 3:静态链表

提示:动态链表虽然“标准”,但是竞赛中一般不用。算法竞赛队内存管理要求不严格,为加快编码速度,一般就在题目允许的存储空间内静态分配,省去了动态分配和释放的麻烦。

静态链表使用预先分配的一段连续空间存储链表。从物理存储的意义上讲,“连续空间”并不符合链表的本义,因为链表的优点就是能克服连续存储的弊端。但是,用连续空间实现的链表,在逻辑上是成立的。具体有两种做法:①定义链表结构体数组,和动态链表的结构差不多;②使用一维数组,直接在数组上进行链表操作。

在此,我们会给出 333 段代码:用结构体数组实现单向静态链表、用结构体数组实现双向静态链表,用一维数组实现单向静态链表。

1. 用结构体数组实现静态链表

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

// 结构体数组实现单向静态链表
#include <bits/stdc++.h>
const int N = 105;								//定义静态链表的空间大小
struct node {									//单向链表int id, nextid;								//单向指针
//  int data;	                                //如有必要,定义一个有意义的数据
} 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 节点,即删除 nownow = nodes[prev].nextid;	            //新的 now}printf("%d", nodes[now].nextid);	        //打印最后一个节点,后面不带空格	return 0;
}

2. 用结构体数组实现双向静态链表

// 结构体数组实现双向静态链表
#include <bits/stdc++.h>
const int N = 105;
struct node {                                                //双向链表int id;                                                  //节点编号//int data;                                                //如有必要,定义一个有意义的数据int preid, nextid;                                       //前一个节点,后一个节点
} 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].preid = i - 1;                              //前节点nodes[i].nextid = i + 1;                             //后节点}nodes[n].nextid = 1;                                     //循环链表:尾指向头nodes[1].preid = n;                                      //循环链表:头指向尾int now = 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;              //删除 nownodes[next].preid = nodes[now].preid;now = next;                                          //新的开始}printf("%d", nodes[now].nextid);                         //打印最后一个节点,后面不带空格return 0;
}

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

这是最简单的实现方法。定义一个一位数组 nodes[]\text{nodes[\,]}nodes[]nodes[i]\text{nodes[}\,i\,\text{]}nodes[i]iii 就是节点的值,而 nodes[i]\text{nodes[}\,i\,\text{]}nodes[i] 的值是下一个节点,它的使用环境很有限,因为他的节点只能存一个数据,就是 iii

// 结构体数组实现双向静态链表
#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;                           //从第一个节点开始while ((n--) > 1) {for (int i = 1; i < m; i++) {prev = now; now = nodes[now];            //数到 m 停下}printf("%d ", now);                          //打印,后面带空格nodes[prev] = nodes[now];                    //跳过 now 节点,即删除 nownow = nodes[prev];                           //新的开始}printf("%d", now);                               //打印最后一个节点,后面不带空格return 0;
}

附:一组洛谷的评测数据

输入

100 3

输出

3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 2 7 11 16 20 25 29 34 38 43 47 52 56 61 65 70 74 79 83 88 92 97 1 8 14 22 28 35 41 49 55 62 68 76 82 89 95 4 13 23 32 44 53 64 73 85 94 5 19 37 50 67 80 98 17 40 59 86 10 46 77 26 71 31 100 58 91

P.S. 这组样例只是帮大家查错,尤其是灰名的,请勿用来打表!


  1. 大数组定义在全局的原因是有的评测环境的栈空间很小,大数组定义在局部占用了栈空间导致爆栈。现在各大在线判题系统(Online Judge, OJ)和比赛应该都会设置编译命令使栈空间等于内存大小,不会出现爆栈。虽然如此,在一些编译器中,大静态数组定义在函数内部会出错。 ↩︎

http://www.dtcms.com/a/296629.html

相关文章:

  • LLM针对隐藏层的特征增强的相关论文
  • Python生成折线图
  • 7.24 C/C++蓝桥杯 | 排序算法
  • 外企本土化布局对国内连接器企业影响几何?
  • 排序初识(上)-- 讲解超详细
  • 【接口自动化】-1- 初识接口
  • VUE的学习
  • shell编程
  • 加密算法-----BCrypt
  • C语言第四章函数
  • Java八大基本类型
  • ICCV 2025 | CWNet: Causal Wavelet Network for Low-Light Image Enhancement
  • 视频剪辑软件使用到的技术栈详解
  • 教育培训系统源码技术拆解:前后端分离、企业培训课程推送机制全解析
  • act_hi_taskinst表历史任务记录不同步,无数据
  • LeetCode 刷题【12. 整数转罗马数字】
  • 解决VSCode无法加载Json架构问题
  • uniapp vue3版本中使用pinia 以及持久化处理 以及在微信小程序ypeError: Cannot read property ‘localStorage‘ of undefined报错
  • 车机版凤凰FM:纯净无广告,免费畅享海量有声资源
  • vue3使用异步加载腾讯地图
  • 奈奎斯特定理与香农公式在说些什么?
  • Linux系统下使用apt下载系统组件对应版本的源码
  • 训练日志7.23
  • k8s常用命令介绍
  • 飞腾D3000PBF和UBOOT配置说明
  • Android15或AndroidU广播的发送流程
  • 阿里云ECS坑之dnf-makecache系统软件更新检测服务
  • Java面试宝典:Spring专题二
  • Access开发一键删除Excel指定工作表
  • Golang实现 - 实现只有表头的 Excel 模板,并在指定列添加了下拉框功能。生成的 Excel 文件在打开时,指定列的单元格会显示下拉选项