这些题都是尝试去找目标数所在位置,而不是去模拟每一次过程
题目链接:OnlineJudge
目录
约瑟夫
简单约瑟夫
复杂约瑟夫
一、递归写法
二、迭代写法
思路:n 的范围足够小,所以我们直接模拟
记录出圈人数out,记录报数count,通过数组的状态判断是否出圈a[105],走到了哪个位置i
#include
#include
#include
#include
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的范围太大了,所以我们要想办法简化算法或者找规律
约瑟夫环——公式法(递推公式)
它的思路相当于,每一次淘汰就把所有人重新编号,胜利者的编号每一次都往前移动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;
}
};
迭代是建立在递推公式上的,如果递归超时或者是想要求得最优解,可以考虑将递归改成迭代
题目链接:https://leetcode-cn.com/problems/elimination-game/
本题与约瑟夫环类似,但又有区别。(疑问:解决约瑟夫环的最优方法是什么?)
不可能把每一个数列出来,然后一次一次地去模拟,所以我们要想办法进行简化
目录
一、自己的思路
二、简化算法
AC代码
三、递归写法
通过自己的观察:
1.每执行一次操作后,剩下的数为等差数列
2.不管n为多少,执行3次后,就只剩下一个数了(×)执行次数:⌊log2n⌋+1
3.n为奇数的时候,会删去末尾的数;n为偶数的时候,不会删去末尾的数
自己有两个思路:
1.草稿纸上推出公式(x)
2.简化算法
正确的思路:
每次删除后,参数的变化是这样的:
d->2d
n->n/2
a0=a0或a0=a0+d
程序最优解的时间复杂度可达到O(logn)
我们把它当做等差数列,等差数列的第一项也就是最后一项就是答案
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);
}
};