约瑟夫问题

目录

一,约瑟夫问题

二,迭代

三,递推式推广

四,约瑟夫推广

五,OJ实战

OpenJ_Bailian 2746 约瑟夫问题

UESTC 965 约瑟夫斯问题

CSU 1125 Joseph Problem Again

力扣 剑指 Offer 62. 圆圈中最后剩下的数字

力扣 1823. 找出游戏的获胜者

POJ 3372 Candy Distribution


一,约瑟夫问题

从围成标有记号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

题目:

Description

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是多少呢.你能帮助大尾巴狼同学吗?

Input

有多组测试数据
每组数据一行,包含一个数字n(1<=n<=10^26).n=0表示输入结束

Output

每组数据输出一行,包含一个数K,使得J(J(J(J...J(n)...)))=K,而且J(K)=K

Sample Input

10
6
0

Sample Output

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. 从第 1 名小伙伴所在位置 开始 。
  2. 沿着顺时针方向数 k 名小伙伴,计数时需要 包含 起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。
  3. 你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
  4. 如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤 2 继续执行。
  5. 否则,圈子中最后一名小伙伴赢得游戏。

给你参与游戏的小伙伴总数 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;
}

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值