约瑟夫问题

约瑟夫问题

人走茶凉 提交于 2019-12-06 04:10:39
全都是抄具体数学的 问题 \(1\) 有 \(n\) 个人围成一圈,顺时针从 \(1\) 到 \(n\) 编号,并从 \(1\) 开始报数,如果一个人报的数字是 \(2\) 的倍数,就把他撒了,后面的人继续,问最终活下来的人的编号 \(Sol1\) 记 \(J(n)\) 为最终活下来人的编号,有 \(J(1)=1\) 如果有 \(2n\) 个人,那么第一圈过去之后所有偶数编号的人都被撒了,那么可以看成还有 \(n\) 个人,只不过第 \(i\) 个人的编号是 \(2i-1\) ,所以有 \(J(2n)=2J(n)-1\) 如果有 \(2n+1\) 个人,那么第一圈过去之后所有偶数编号的人都被撒了,下一圈一开始 \(1\) 号被撒了,那么可以看成还有 \(n\) 个人,第 \(i\) 个人的编号是 \(2i+1\) ,所以有 \(J(2n)=2J(n)+1\) 如果我们记 \(k\) 为 \(n\) 的二进制最高位,有 \(J(n)=2(n-2^k)+1\) 打表证明即可 ,归纳证明即可 问题 \(2\) 有 \(n\) 个人围成一圈,顺时针从 \(1\) 到 \(n\) 编号,并从 \(1\) 开始报数,如果一个人报的数字是 \(3\) 的倍数,就把他撒了,后面的人继续,问最终活下来的人的编号 \(Sol2\) 如果还是按上面的递归式的方法不用做了 考虑一种新的方法, \(1\)

P1996 约瑟夫问题

匆匆过客 提交于 2019-12-04 00:09:58
题目背景 约瑟夫是一个无聊的人!!! 题目描述 n个人(n<=100)围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,……依次类推,直到所有的人都出圈,请输出依次出圈人的编号. 输入格式 n m 输出格式 出圈的编号 输入输出样例 输入 #1 复制 10 3 输出 #1 复制 3 6 9 2 7 1 8 5 10 4 说明/提示 m, n \le 100 m , n ≤ 1 0 0 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; queue<int>q; int n,m; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ q.push(i); } while(!q.empty()){ for(int i=1;i<m;i++){ q.push(q.front()); q.pop(); } printf("%d ",q.front()); q.pop(); } return 0; }    来源: https://www.cnblogs.com/xiongchongwen/p

约瑟夫问题

回眸只為那壹抹淺笑 提交于 2019-12-01 09:53:14
Josephu(约瑟夫,约瑟夫环)问题 问题表述为:设编号为1,2,...,n的n个人围坐一圈,约定编号为K(1<=k<=n)的人开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依此类推,直到所有人出列为止,由此产生一个出队编号的序列 (一)约瑟夫问题-创建环形链表的思路图解 (二)约瑟夫问题-小孩出圈的思路分析图 package linkedlist; public class Josepfu { public static void main(String[] args) { //测试构建环形链表与显示环形链表是否正确 CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); circleSingleLinkedList.addBoy(5);//加入五个节点 circleSingleLinkedList.showBoy(); //测试小孩出圈是否正常 circleSingleLinkedList.countBoy(1, 2, 5); } } //创建环形的单向链表 class CircleSingleLinkedList{ //创建first节点 private Boy first=new Boy(-1); //添加节点构建成环形链表 public

约瑟夫问题

泄露秘密 提交于 2019-11-30 06:35:15
概念介绍   有同学想了解约瑟夫问题,今天它来了!直接进入主题,什么是约瑟夫问题? 约瑟夫问题:N个人围成一圈,从约定编号为K的人开始报数,第M个将被杀掉,依次类推,最后剩下一个,其余人都将被杀掉 。   直接上图展示, 初始化状态: 假设n=6,总共有6个人,k=1,从第一个人开始报数,m=5,每次数五个 。    第一次报数:从一号开始,数五个数,1-2-3-4-5,数完五个数,五号被杀死,第一次报数后,剩余人数如下 。       第二次报数: 从被杀死的五号的下一位开始报数,也就是六号,数五个数,6-1-2-3-4,数数完毕,四号被杀死,第二次报数后,剩余人数如下 。       第三次报数: 从被杀死的四号的下一位开始报数,同样是六号,数五个数,6-1-2-3-6,数数完毕,六号被杀死,第三次报数后,剩余人数如下 。       第四次报数: 从被杀死的六号的下一位开始报数,也就是一号,数五个数,1-2-3-1-2,数数完毕,二号被杀死,第四次报数后,剩余人数如下 。       第五次报数: 从被杀死的二号的下一位开始报数,也就是三号,数五个数,3-1-3-1-3,数数完毕,三号被杀死,只剩下一号,Game Over!    代码实现   实现思路:用单向循环链表来表示圈,将人杀死后修改链表上的节点即可。   第一步:构建节点上对象。 1 class Person {

约瑟夫问题升级版

久未见 提交于 2019-11-30 00:07:54
题目 先分析一下题目 首先有一个初始数字,用来判断下一个出列的人是谁,当这个人出列之后,他的密码就会被当做新的数字来判断下一个是谁出局。直到所有的人都出列。 可以建立一个Person类,用来存放,密码,序号和Person对象的指针。 public static class Person{ private int id; //序号 private Person next; //对象指针 private int password; //密码 public Person(int id,int password){ this.id = id; this.password = password; } } public static void main(String[] args){ Scanner sca = new Scanner(System.in); System.out.println("总人数:"); int n = sca.nextInt(); //输入完总人数后,先保存第一个人的信息,id和他的密码,然后将头节点置为第一个人 System.out.println("第1个人的密码:"); int A1 = sca.nextInt(); Person p1 = new Person(1, A1); Person index = p1; // 然后用for来继续保存其余人的信息

bailian.openjudge 2746:约瑟夫问题

牧云@^-^@ 提交于 2019-11-29 14:24:49
http://bailian.openjudge.cn/practice/2746 描述 约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。 输入 每行是用空格分开的两个整数,第一个是 n, 第二个是 m ( 0 < m,n <=300)。最后一行是: 0 0 输出 对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号 样例输入 6 2 12 4 8 3 0 0 样例输出 5 1 7 # include <iostream> # include <string.h> # include <stdio.h> using namespace std ; bool flag [ 301 ] ; int main ( ) { int n , m ; cin >> n >> m ; while ( n != 0 && m != 0 ) { int index = - 1 ; int count = 0 ; int i ; memset ( flag , false , sizeof ( flag ) ) ; for ( i = 1 ; i < n ; i ++ ) { count = 0

1012 Joseph 模拟

让人想犯罪 __ 提交于 2019-11-29 11:45:37
本题为经典约瑟夫问题。 有约瑟夫经典公式:ans[i]=(ans[i-1]+m)%i表示i个人报数,最终胜利者的编号。 本题与原本约瑟夫问题的不同点在于共有2k个人,要杀掉后k人保留前k人。而原约瑟夫问题为杀掉除自己外的所有人。所以在写代码时应注意。 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int main(){ 5 int k=0,n=0,ans[15],count[20],m;//ans[i]表示留下k个人时且杀死所有坏人的最小编号m。 6 //count[i]表示i个人报数每报到m时杀死人的编号。 7 count[0]=0; 8 memset(ans,0,sizeof(ans)); 9 while(cin>>k){ 10 if(k==0) 11 break; 12 m=1; 13 n=2*k; 14 for(int i=1;i<=k;i++){ 15 count[i]=(count[i-1]+(m-1))%(n-i+1);//后面的除数为求编号时,考虑到m可能大于存活者人数,所有人轮过一次后新一轮才进行杀人的情况。 16 if(count[i]<k){//杀到好人了,则此m不符合要求,m递增,i重头再来。 17 m++; 18 i=0; 19 } 20 } 21 ans[k]=m; 22

约瑟夫问题

孤街浪徒 提交于 2019-11-29 03:23:57
也许更好地阅读体验 \(\mathcal{Description}\) 经典的约瑟夫问题 一开始有 \(n\) 个人围成一个圈,他们的编号从 \(0\) 到 \(n-1\) , 从 \(1\) 开始顺时针报数, 报出 \(m\) 的人被机关处决. 然后下一个人再从 \(1\) 开始报数, 直到只剩下一个人. 问最后剩下的人的编号. \(n\leq 10^{18},\ m\leq 1000\) \(\mathcal{Solution}\) 那些算法书总是把约瑟夫问题作为链表的例题来讲,初学者接触链表时还不会递推,于是一直以为只是一道链表题... 这么大的数据,当然不能链表模拟 考虑递推,还是那句话,递推的东西总是有联系的 递推时往往假设一个状态是已知的,然后试图推另一个状态,如果有递推式的话,我们就只要将最简单的状态手动求出即可 假设现在有 \(n\) 个人,第一个被处决的人的编号肯定是 \(k=(m-1)\%n\) 当编号为 \(k\) 的人被处决后,接下来我们认为是从原编号为 \(k+1\) 的人开始重新编号,然后就是一个 \(n-1\) 个人的约瑟夫问题了 当然,这 \(n-1\) 个人的答案肯定不是最终答案,因为他是重新编号后的答案,所以我们要将其恢复原编号 因为新编号为 \(0\) 的人前面有 \(m-1\) 个人,也就是编号左移了 \(m\) ,所以最终的答案应该要加上

【数据结构】P1996 约瑟夫问题

泪湿孤枕 提交于 2019-11-27 19:53:44
【题目链接】 https://www.luogu.org/problem/P1996 题目描述 n个人(n<=100)围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,…… 依次类推,直到所有的人都出圈,请输出依次出圈人的编号. 输入格式 n m 输出格式 出圈的编号 输入输出样例 输入 #1 10 3 输出 #1 3 6 9 2 7 1 8 5 10 4 说明/提示 100 ≤ m,n ≤ 100 【题解】 问题其实并不困难,但是目的就是利用题目来锻炼自己的数据结构。 给出一题五解的做法。 【解法一】 利用STL里面的list,注意指针到了链表尾部要指回链表的头部。 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m; 5 6 int main() 7 { 8 list<int> List ; 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;i++) List.push_back(i); 11 list<int> ::iterator it = List.begin(),tmp; 12 13 for(int i=1;i<=n;i++){ 14 for(int j=0;j<m-1;j++)

约瑟夫问题

杀马特。学长 韩版系。学妹 提交于 2019-11-27 08:42:59
先贴出代码 #include<malloc.h> #include<iostream> using namespace std; typedef struct node{ int num; struct node *next; } SLink; //先创建 void init(SLink *&L,int *a,int l){ L = (SLink *)malloc(sizeof(SLink)); L->num=a[0]; L->next=L; SLink *s,*pre=L; for(int i=1;i<l;i++){ s = (SLink *)malloc(sizeof(SLink)); s->next=pre->next; pre->next=s; s->num=a[i]; pre=s; // cout<<a[i]; } } //输出 void show(SLink *&L,int n){ int start=1; SLink *pre=L,*p=pre->next; while(pre->next!=pre){ //找到n的前结点n-1 while(start<n-1){ pre=p; p=p->next; start++;//到n-1、跳出循环 } //到这里了 删除p 将pre指向p的后结点 pre->next=p->next; cout<<p->num; //free