经典递归题 扩充序列 两种做法


一道经典递归题,两种做法,常规递归做法和模拟数学规律解法

3695. 扩充序列 - AcWing题库

扩充序列

样例解释

对于样例 1,经过 2 次扩充,得到序列 [1,2,1,3,1,2,1]其第 2 个元素为 2。

对于样例 2,经过 3次扩充,得到序列 [1,2,1,3,1,2,1,4,1,2,1,3,1,2,1]其第 8个元素为 4。

思路一

先看序列的长度。N=1时,序列长度为1,N=2时,序列长度为3

N=3时,序列长度为7,N=4时,序列长度为15

不难看出规律

假设长度为Len,则N和长度的关系为

Len=pow(2,n)-1;

则我们在算K时,有三种情况

情况1

是k=pow(2,n-1),则K是最中间那个数,也就是扩容第N-1次时,新增的那个数,通过样例可以看出,新增的数,就是N本身,例如扩容3次,新增的数是4

1,2 ,1,3, 1,2, 1,4, 1,2, 1,3 ,1,2 ,1

所以直接返回N,即是结果,k的值

情况2

是k<pow(2,n-1),接着计算n=n-1时,k的值是多少

情况3

是k>pow(2,n-1),只需要把k-(pow(2,n-1)),然后按情况2计算即可,因为中点左右两边的序列,是一样的

通过这三种情况,发现可以直接递归做,看一下数据范围

n<50,最多是O(4n),时间复杂度没问题,但是pow(2,50)爆int,需要longlong

实现代码1

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll ;
ll n,k,res;
ll dfs(ll k){
    if(k==pow(2,n-1))return n;//情况一,确定是新增数,直接返回
    //如果是情况三,则变成情况二
    if(k>pow(2,n-1))k-=pow(2,n-1);
    //缩小范围,只需要查找在上次扩充之前出现的数
    n-=1;
    //递归
    dfs(k);
}
int main(){
    cin>>n>>k;
    res=dfs(k);
    cout<<res;
    return 0;
}

思路二

发现数列规律

所有奇数位都是1,也就是如果k%2!=0,则k的值为1,这里我们只看值,则k是最初始的数

反之

如果k%2==0,则k一定是扩充的数,那么我们利用k,就可以推断出,k位对应的值,是在第几次扩充出现的

例如

1,2 ,1,3, 1,2, 1,4, 1,2, 1,3 ,1,2 ,1

红4是第八位,对应的k是8,k/2=4,4/2=2,2/2=1,k能除以3次,所以k对应的值,是第三次扩充后出现

1,2 ,1,3, 1,2, 1,4, 1,2, 1,3 ,1,2 ,1

红2是第十位,k=10,10/2=5,5是奇数,所以不用除了,k除了一次,k对应的值,是在第一次扩充时出现的

1,2 ,1,3, 1,2, 1,4, 1,2, 1,3 ,1,2 ,1

红3是第十二位,k=12,12/2=6,6/2=3,3是奇数,不用除了,k除了一次,k对应的值,是在第二次扩充时出现的

第三次扩充出现的值是4,第二次扩充出现的值是3,第一次扩充出现的值是2

所以我们只需要计算,k对应的值是第几次扩充时出现的,然后+1即可

实现代码二

#include<iostream>
using namespace std;
typedef long long ll ;
    ll n,k,res=0;
int main(){
    cin>>n>>k;
     //k&1是位运算,如果&1为真,则k%2!=0
    while(!(k&1))k>>=1,res++;
    cout<<res+1;
    return 0;
}
  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值