约瑟夫问题方法总结
n个人围成一个圈,每个人分别标注为1、2、...、n,要求从1号从1开始报数,报到k的人出圈,接着下一个人又从1开始报数,如此循环,直到只剩最后一个人时,该人即为胜利者。例如当n=10,k=4时,依次出列的人分别为4、8、2、7、3、10,9、1、6、5,则5号位置的人为胜利者。给定n个人,请你编程计算出最后胜利者标号数。(要求用单循环链表完成。)
第一行为人数n; 第二行为报数k
10
4
对于约瑟夫问题当前实现方法大概有两种:
一:模拟:
链表模拟:
1 #include<stdio.h>
2 #include<malloc.h>
3 typedef struct List
4 {
5 int data;
6 struct List *next;
7 }LinkList;
8 int main()
9 {
10 LinkList *L,*r,*s;
11 L = (LinkList *)malloc(sizeof(LinkList));
12 r =L;
13 int n,i;
14 int k;
15 scanf("%d%d",&n,&k);
16 for(i = 1;i<=n;i++) ///尾插法建立循环链表
17 {
18 s = (LinkList *)malloc(sizeof(LinkList));
19 s->data = i;
20 r->next = s;
21 r= s;
22 }
23
24 r->next =L->next; //让最后一个链表的下一个节点指向开头
25 LinkList *p;
26 p = L->next;
27
28 while(p->next != p) //开始模拟(判断条件要注意:因为最后肯定剩下一个人, 所以最后一个数据的next还是他本身)
29 {
30 for(i = 1;i<k-1;i++)
31 {
32 p = p->next; //每k个数死一个人
33 }
34 p->next = p->next->next; //将该节点从链表上删除。
35 p = p->next;
36 }
37 printf("%d",p->data);
38 return 0;
39 }
数组模拟:
1 #include<stdio.h>
2 int main()
3 {
4 int n, k;
5 scanf("%d%d", &n, &k);
6 int i;
7 int a[1001];
8 int dead = 0; //表示已经死了多少人
9 int num = 0; //num模拟没有被杀的人的喊数
10 for (i = 1; i<=n; i++) //开始时每个人都可以报数,为了能得到最后一个人的编号,我们让初始值为i下标
11 {
12 a[i] = i;
13 }
14 for (i = 1;; i++)
15 {
16 if (i > n)
17 {
18 i = i%n; //如果大于总人数,我们就从头开始
19 }
20
21 if (a[i] > 0) //如果当前这个人没有死,就报数
22 num++;
23
24 if (k == num && dead != n-1) //如果当前这个人报的数等于k 并且没有已经死亡n-1个人
25 {
26 num = 0;
27 a[i] = 0;
28 dead++;
29 }
30 else if(k == num && dead == n-1) //如果这个人报数等于k,并且已经死了n-1个人,说明当前这个人就是最后的一个活着的了。。
31 {
32 printf("%d", a[i]);
33 break;
34 }
35
36 }
37 return 0;
38 }
二、公式法(即递推):
递推过程:
(1)第一个被删除的数为(m-1)%n;
(2)设第二次的开始数字为k,
做下映射:(即将数字的排列计算还是从0开始)
k--->0
k+1--->1
k+2--->2
--- ---
k-2--->n-2
此时剩下n-1个人 ,假如我们已经知道了n-1个人时,最后胜利者的编号为x,利用映射关系逆推,就可以得出n个人时,胜利者的编号为(x+k)%n(要注意的是这里是按照映射后的序号进行的)
其中k=m%n。
代入
(x+k)%n<=>(x+(m%n))%n<=>(x%n + (m%n)%n)%n<=> (x%n+m%n)%n <=> (x+m)%n
(3)第二个被删除的数为(m-1)%n-1
(4)假设第三轮的开始数字为o,那这n-2个数构成的约瑟夫环为o,o+1,o+2,...,o-3,o-2。
映射
o--->0
o+1--->1
o+2--->2
--- ---
o-2--->n-3
这是一个n-2个人的问题。假设最后胜利者为y,那么n-1个人时,胜利者为(y+o)%(n-1),其中o等于m%(n-1)。代入可得(y+m)%(n-1)
要得到n-1个人问题的解,只需要得到n-2个人问题的解,倒退下去。只有一个人时,胜利者就是编号0.小面给出递推式:
f(1)=0;
f(i)=(f[i-1]+m)%i;(i>1)
这个公式的思想:
现在假设n=10
0 1 2 3 4 5 6 7 8 9
k=3
第一个人出列后的序列为:
0 1 3 4 5 6 7 8 9
即: 3 4 5 6 7 8 9 0 1(1式)
我们把该式转化为: 0 1 2 3 4 5 6 7 8 (2式)
则你会发现: ((2式)+3)%10则转化为(1式)了
也就是说,我们求出9个人中第9次出环的编号,最后进行上面的转换就能得到10个人第10次出环的编号了
设f(n,k,i)为n个人的环,报数为k,第i个人出环的编号,则f(10,3,10)是我们要的结果
当i=1时, f(n,k,i) = (n+k-1)%n
当i!=1时, f(n,k,i)= ( f(n-1,k,i-1)+k )%n
1 #include<stdio.h>
2 int main()
3 {
4 int n, m,i,s=0;
5 scanf("%d%d",&n,&m);
6 for(i=2;i<=n;i++)
7 s=(s+m)%i;
8 printf("%d", s+1);
9 return 0;
10 }
说一下:
for(i=2;i<=n;i++) s=(s+m)%i;
这个式子:
首先从2开始,因为1个人的时候报的数字的人为0号,结果已经确定了。不需要从i=0开始,要注意的是序列从0开始编号的,所以最后的输出结果也要加1.
s表示的是上一轮的结果,m代表是每多少个人出列一次,i代表当前已经出列了多少个人。
整个式子就是根据上一个的出列数和已经出列的人数来算的。
如果还不懂就仔细琢磨哦。
- 一键清理应用数据或者清除应用缓存的方法
- 开发者必知:谷歌做了一个艰难的决定
- React编程思想
- 基于 React + Webpack 的音乐相册项目(下)
- Python中Keras深度学习库的回归教程
- Apache Spark 1.1中的统计功能
- 一次关闭所有Activity和连续点击两次返回键关闭程序的方法
- 解决TextView排版混乱或者自动换行的问题
- 怎样设置EditText内部文字被锁定不可删除和修改,而文字只能在后面输入
- 教你如何使用微信网页版“抓取”微信撤回消息
- 移动开发之实现图片轮播效果
- 浅谈Andorid开发中的MVP模式
- jsp标准标签库整理(JSTL)
- java Map的简单运用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- SAP Spartacus central configuration - B2cStorefrontModule
- Android自定义控件实现折线图
- 关于Angular Component changeDetection策略设置成OnPush的一个单元测试局限性
- SAP Spartacus CmsService.Components数据结构
- Android同时安装Release和Debug版本的方法
- Django 后台带有字典的列表数据与页面js交互实例
- SAP Spartacus CmsService的CmsActions.LoadCmsComponent
- SAP Spartacus OccEndpointsService单元测试的依赖注入
- Python利用PyPDF2库获取PDF文件总页码实例
- TypeScript里get属性的实现
- SAP Spartacus OccEndpointsService单元测试之getBaseEndpoint
- html+css入门基础案例之圣诞那些事
- 建议收藏备查!MySQL 常见错误代码说明
- html+css入门基础案例之页面设计
- Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香...