约瑟夫以及其变异体总结
Posted yonuyeung
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了约瑟夫以及其变异体总结相关的知识,希望对你有一定的参考价值。
学编程最重要的是学思想!!!
这些题都是尝试去找目标数所在位置,而不是去模拟每一次过程
目录:
约瑟夫
LC 390.消除游戏
约瑟夫
简单约瑟夫
题目链接:OnlineJudge
目录
思路:n 的范围足够小,所以我们直接模拟
记录出圈人数out,记录报数count,通过数组的状态判断是否出圈a[105],走到了哪个位置i
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int main()
int n,m,s;
cin>>n>>m>>s;
int out,count,a[105],i;
memset(a,-1,sizeof(a));
out=0; count=0; i=s;
while(out<n)
for(;i<=n;i++)
if(a[i]) count++;
if(count==m)
printf("%3d",i); count=0; out++; a[i]=0;
i=1;
return 0;
复杂约瑟夫
题目链接:力扣
思路:n的范围太大了,所以我们要想办法简化算法或者找规律
它的思路相当于,每一次淘汰就把所有人重新编号,胜利者的编号每一次都往前移动m个单位。
拓展:迭代和递归的理解和区别
一、递归写法
class Solution
public:
int f(int n,int m)
if(n==1) return 0;
else return (f(n-1,m)+m)%n;
int lastRemaining(int n, int m)
int x;
x=f(n,m);
return x;
;
二、迭代写法
class Solution
public:
int lastRemaining(int n, int m)
int f = 0;
for (int i = 2; i != n + 1; ++i)
f = (m + f) % i;
return f;
;
迭代是建立在递推公式上的,如果递归超时或者是想要求得最优解,可以考虑将递归改成迭代
LC 390.消除游戏
题目链接:https://leetcode-cn.com/problems/elimination-game/
本题与约瑟夫环类似,但又有区别。(疑问:解决约瑟夫环的最优方法是什么?)
不可能把每一个数列出来,然后一次一次地去模拟,所以我们要想办法进行简化
目录
一、自己的思路
通过自己的观察:
1.每执行一次操作后,剩下的数为等差数列
2.不管n为多少,执行3次后,就只剩下一个数了(×)执行次数:⌊log2n⌋+1
3.n为奇数的时候,会删去末尾的数;n为偶数的时候,不会删去末尾的数
自己有两个思路:
1.草稿纸上推出公式(x)
2.简化算法
二、简化算法
正确的思路:
每次删除后,参数的变化是这样的:
d->2d
n->n/2
a0=a0或a0=a0+d
程序最优解的时间复杂度可达到O(logn)
我们把它当做等差数列,等差数列的第一项也就是最后一项就是答案
AC代码
class Solution
public:
int lastRemaining(int n)
int loop_cnt=0; //用来判断是从左到右还是从右到左
int a0=1,d=1;
while(n!=1)
if(n%2!=0)
a0=a0+d;
else
bool left_to_right = (loop_cnt%2==0);
if(left_to_right) //左往右
a0=a0+d;
else
a0=a0;
n/=2;
d*=2;
loop_cnt++;
return a0;
;
当然可以写得更简单一点:
class Solution
public:
int lastRemaining(int n)
int a0 = 1, d = 1;
bool left_to_right = true;
while(n > 1)
if(left_to_right || n % 2 == 1)
a0 += d;
n /= 2;
d *= 2;
left_to_right = ! left_to_right;
return a0;
;
三、递归写法
作者:daydayUppp
因为每次往下递归 , 数据规模减少 一半 , 所以 时间复杂度 为 : O(logn)
class Solution
public:
int help(int n,bool L2R)
// L2R = true 从左到右
if(n == 1) return 1;// 最小子问题
if(L2R) return 2 * help(n / 2,!L2R);// 左到右
// 1 2 3 4 5 6 7 8
// 2 4 6 8
// 2*(1 2 3 4)
// 右到左
// 奇数长度的情况
if(n & 1) return 2 * help(n / 2,!L2R);
// 1 2 3 4 5 6 7
// 2 4 6
// 2*(1 2 3 )
// 偶数长度的情况
return help(n / 2,!L2R) * 2 - 1;
// 1 2 3 4 5 6 7 8
// 1 3 5 7
// 2*(1 2 3 4) - 1
int lastRemaining(int n)
return help(n,true);
;
LeetCode里面还有很多变态的解法,这里就不研究了。
以上是关于约瑟夫以及其变异体总结的主要内容,如果未能解决你的问题,请参考以下文章