目录
一,约瑟夫问题
从围成标有记号1到 n 的圆圈的 n 个人开始,每隔一个删去一 个人,直到只有一个人幸存下来.例如 n = 10的起始图形:
消去的顺序是2,4,6,8,10,3,7,1,9,于是5幸存下来.问题:确定幸存者的号码 J ( n) .
首先第一步就是删除所有偶数,所以 J(2n) = 2J(n) - 1
对于奇数的情形,结果又如何呢?对于 2n+1个人,显然标号为1的人恰好是在标号为 2n 的人后面被删除,剩下的是:
所以 J(2n+1) = 2J(n) + 1
递推式的解是
表示成二进制是
也就是说,n 向左循环移动一位就得到 J ( n)
二,迭代
J函数不停的迭代,最终会稳定在k,J(k)=k
假设n的二进制中1的个数是a,那么从n开始迭代,最后的k=2^a-1
三,递推式推广
如果把递推式推广成
可以解出来:
其中n = 2^m + L, 0<=L<2^m
表示成泛二进制(每一位上的数字不一定是0或1)
进一步推广:
泛c进制的解是
四,约瑟夫推广
从围成标有记号1到 n 的圆圈的 n 个人开始,每隔m个删去一 个人,直到只有一个人幸存下来.求 f(n)
递推式:
f(n+1) = (f(n)+m) % (n+1)
图解:(以n=10,m=4为例)
五,OJ实战
OpenJ_Bailian 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<stdio.h>
int main()
{
int n, m, i, s = 0;
while (1)
{
s = 0;
scanf("%d%d", &n, &m);
if (m == 0)break;
for (i = 2; i <= n; i++)s = (s + m) % i;
printf("%d\n", s + 1);
}
return 0;
}
UESTC 965 约瑟夫斯问题
题目:
据传说,罗马人攻夺Jotapat后,Josephus和朋友两人与其他n个犹太人避难到一个洞穴里。使Josephus非常反感的是,他发现除了他自己和朋友外,其余的都决心殉难以免落入征服者的手中。他不敢太公然表示反对而只好同意了,但他坚持这一行动必须有条不紊地进行,并且建议大家坐成一圆圈,然后从洞口的人开始顺时针报数,数到的人就杀掉,然后又从下一个人开始从报数,直到最后一个人去自杀。问Josephus应该把自己和朋友放在第几个位置,才能避免被杀掉。注意:本题要求用链表实现。
Input
本题有多组测试数据,第一行是测试数据组数,下面行的每一行有两个用一个空格隔开的数、,表示犹太人数和报的数。其中,。
Output
输出共有行,依次对应输入行。每行有两个数,从小到大排列,并用空格隔开。注意第二个数后没有空格。
Sample Input
1
2 2
Sample Output
1 3
Hint
位置是从洞口顺时针从依次编号,报数也是从位置的人顺时针报数。
代码:
#include<stdio.h>
int main()
{
int T,n, m;
scanf("%d", &T);
while (T--)
{
int i, s1 = 0, s2 = 1;
scanf("%d%d", &n, &m);
for (i = 3; i <= n+2; i++)s1 = (s1 + m) % i, s2 = (s2 + m) % i;
if (s1 > s2)s1 ^= s2 ^= s1 ^= s2;
printf("%d %d\n", s1 + 1, s2 + 1);
}
return 0;
}
CSU 1125 Joseph Problem Again
题目:
026城市的大尾巴狼和秃尾巴狼是好朋友,有一天,秃尾巴狼给大尾巴狼说了有关约瑟夫环的问题:n个编号依次为1,2....n的人站成一个圈,他们按序号大小顺时针的站着.然后从1号开始报数,报到2的人就会被杀掉,然后再从1开始报数.直到只剩下一个人为止.剩下这个人的编号我们定义为J(n).我们可以知到J(1)=1,J(5)=3,J(6)=5.大尾巴狼开始冥思苦想起来.突然他发现,如果每次用J(n)当成现在的n的话,那么最后会得到一个fixed number.比如说J(2)=1.J(J(2))=1,J(J(J(...J(2)...)))=1.秃尾巴同意大尾巴狼的这个想法,但是大尾巴狼还是不满足,他想知道对于一个给定的n,那么最后会得到的fixed number是多少呢.你能帮助大尾巴狼同学吗?
有多组测试数据
每组数据一行,包含一个数字n(1<=n<=10^26).n=0表示输入结束
每组数据输出一行,包含一个数K,使得J(J(J(J...J(n)...)))=K,而且J(K)=K
10
6
0
3
3
import java.util.*;
import java.math.BigInteger;
public class Main{
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
while(true) {
BigInteger n=cin.nextBigInteger();
if(n.compareTo(BigInteger.ZERO)==0)break;
BigInteger ans=BigInteger.ONE;
while(n.compareTo(BigInteger.ZERO)==1)
{
if(n.mod(BigInteger.valueOf(2)).compareTo(BigInteger.ONE)==0)
ans=ans.multiply(BigInteger.valueOf(2));
n=n.divide(BigInteger.valueOf(2));
}
ans=ans.subtract(BigInteger.ONE);
System.out.println(ans.toString());
}
}
}
力扣 剑指 Offer 62. 圆圈中最后剩下的数字
0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
示例 2:
输入: n = 10, m = 17
输出: 2
限制:
1 <= n <= 10^5
1 <= m <= 10^6
class Solution {
public:
int lastRemaining(int n, int m) {
int s = 0;
for (int i = 2; i <= n; i++)s = (s + m) % i;
return s;
}
};
力扣 1823. 找出游戏的获胜者
共有 n
名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1
到 n
编号。确切地说,从第 i
名小伙伴顺时针移动一位会到达第 (i+1)
名小伙伴的位置,其中 1 <= i < n
,从第 n
名小伙伴顺时针移动一位会回到第 1
名小伙伴的位置。
游戏遵循如下规则:
- 从第
1
名小伙伴所在位置 开始 。 - 沿着顺时针方向数
k
名小伙伴,计数时需要 包含 起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。 - 你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
- 如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤
2
继续执行。 - 否则,圈子中最后一名小伙伴赢得游戏。
给你参与游戏的小伙伴总数 n
,和一个整数 k
,返回游戏的获胜者。
示例 1:
输入:n = 5, k = 2 输出:3 解释:游戏运行步骤如下: 1) 从小伙伴 1 开始。 2) 顺时针数 2 名小伙伴,也就是小伙伴 1 和 2 。 3) 小伙伴 2 离开圈子。下一次从小伙伴 3 开始。 4) 顺时针数 2 名小伙伴,也就是小伙伴 3 和 4 。 5) 小伙伴 4 离开圈子。下一次从小伙伴 5 开始。 6) 顺时针数 2 名小伙伴,也就是小伙伴 5 和 1 。 7) 小伙伴 1 离开圈子。下一次从小伙伴 3 开始。 8) 顺时针数 2 名小伙伴,也就是小伙伴 3 和 5 。 9) 小伙伴 5 离开圈子。只剩下小伙伴 3 。所以小伙伴 3 是游戏的获胜者。
示例 2:
输入:n = 6, k = 5 输出:1 解释:小伙伴离开圈子的顺序:5、4、6、2、3 。小伙伴 1 是游戏的获胜者。
提示:
1 <= k <= n <= 500
进阶:你能否使用线性时间复杂度和常数空间复杂度解决此问题?
和剑指 Offer 62. 圆圈中最后剩下的数字几乎一模一样。
impl Solution {
pub fn find_the_winner(n: i32, k: i32) -> i32 {
let mut s=0;
let mut id=2;
while id<=n{
s=(s+k)%id;
id+=1;
}
return s+1;
}
}
POJ 3372 Candy Distribution
Description
N children standing in circle who are numbered 1 through N clockwise are waiting their candies. Their teacher distributes the candies by in the following way:
First the teacher gives child No.1 and No.2 a candy each. Then he walks clockwise along the circle, skipping one child (child No.3) and giving the next one (child No.4) a candy. And then he goes on his walk, skipping two children (child No.5 and No.6) and giving the next one (child No.7) a candy. And so on.
Now you have to tell the teacher whether all the children will get at least one candy?
Input
The input consists of several data sets, each containing a positive integer N (2 ≤ N ≤ 1,000,000,000).
Output
For each data set the output should be either "YES" or "NO".
Sample Input
2
3
4
Sample Output
YES
NO
YES
我一步步推导,最后得到结论,就是要判断是不是满足,对于任意的整数a,b,只要n不整除(a-b)就有2n不整除(a-b)*(a+b+1)
然而,我还是只能得到平方次数的算法,超时。
后来听了学长的话才突然发现,这个式子真的很好解啊啊啊啊。。。
如果n有1个奇因数p,那么取a-b=n/p,a+b+1=2*p,就发现n是不满足的。
所以n是2的幂。
代码:
#include<iostream>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
if ((n&-n) == n)cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}