疯狂整数的统计(题解)

95 阅读3分钟

问题描述

小C发现了一类特殊的整数,他称之为“疯狂整数”。疯狂整数是指只包含数字 '1' 和 '2' 的整数。举个例子,数字 12 和 121 都是疯狂整数。

现在,给定一个整数 N,你的任务是计算出所有小于或等于 N 的非负疯狂整数的数量。


测试样例

样例1:

输入:N = 21
输出:5

样例2:

输入:N = 50
输出:6

样例3:

输入:N = 5
输出:2

疯狂整数是一种特殊的整数序列,具体包含了1、2、11、12、21、22等数字。我们可以通过对这些疯狂整数进行分组来更好地理解它们的生成规律。具体来说,疯狂整数可以按照位数进行分组:

第一组:单位数的疯狂整数,包括1和2。 第二组:由第一组的数字后面加上1或2生成的两位数疯狂整数,包括11、12、21和22。 第三组:由第二组数字后面加上1或2生成的三位数疯狂整数,例如111、112、121、122、211、212、221、222。 通过观察,我们可以发现,n位数的疯狂整数都是通过在(n-1)位数的疯狂整数后面添加1或2来生成的。这一规律使得我们可以用一个队列来有效地存储和生成这些疯狂整数。

具体的生成方法如下:

初始化队列:首先将1和2放入队列中。 出队和入队:反复执行以下操作:从队列中取出一个数字,将其末尾添加1和2生成新的数字,然后将生成的数字再放入队列中。 终止条件:这一过程持续进行,直到当前出队的数字大于N为止。 时间复杂度分析 我们注意到,随着位数的增加,疯狂整数的数量呈指数增长:

一位数的疯狂整数有2个(1和2)。 二位数的疯狂整数有4个(11、12、21、22)。 三位数的疯狂整数有8个(111、112、121、122、211、212、221、222)。 以此类推,n位数的疯狂整数总数为2^n

因此,假设N是一个18位数,那么我们最多会生成 2^19 −2 个疯狂整数。值得注意的是, 2^19≈524288,这个数量远小于1百万,因此在计算和生成这些数字的过程中,时间复杂度是可接受的,不会导致超时。

通过这样的分析,我们不仅理解了疯狂整数的生成方式,也清晰地认识到其时间复杂度的可控性。这使得使用队列来生成疯狂整数成为一种高效且简单的解决方案。

代码实现如下:

#include <iostream>
#include <vector>
#include <string>
#include <queue>
using namespace std;

int solution(int N) {
    queue<int> q;
    q.push(1);
    q.push(2);
    int ans = 0;
    while(1){
        int tmp = q.front();
        q.pop();
        if(tmp <= N){
            ans++;
            q.push(tmp*10 + 1);
            q.push(tmp*10 + 2);
        }
        else break;
    }
    return ans;
}

int main() {
    std::cout << (solution(21) == 5) << std::endl;
    std::cout << (solution(50) == 6) << std::endl;
    std::cout << (solution(5) == 2) << std::endl;
}