20180907虎牙面试编程题2

输入描述

一个正整数n,范围在[1, 231 2 31 -1]。

输出描述

从0到n这n+1个数中,有多少个数满足条件:该数的二进制表示没有3个连续的1。

示例

输入一

9

输出一

9

0-9这十个数中只有7(111)不满足条件,因此输出为9。

输入二

17

输出二

15

0-17这十八个数中,7(111)、14(1110)、15(1111)不满足条件,因此输出为15。

代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

int bits(unsigned int n) {
    int ans = 0;
    while (n > 0) {
        ans++;
        n >>= 1;
    }
    return ans;
}

int main() {
    unsigned int n;
    cin >> n;
    clock_t t = clock();
    int len = bits(n);
    vector<int> dp(32, 0);
    dp[0] = 2;
    dp[1] = 4;
    dp[2] = 7;
    for (int i = 3; i < len-1; i++) {
        dp[i] = dp[i-1]+dp[i-2]+dp[i-3];
    }
    unsigned int ans = 0;
    int count = 0;
    for (int i = len-1; i >= 0; i--) {
        if ((n>>i)&1) {
            count++;
            if (count == 3) {
                ans += (i == 0 ? 1 : dp[i-1]);
                break;
            } else {
                ans += (i == 0 ? 2 : dp[i-1]);
            }
        } else {
            count = 0;
        }
    } 

    t = clock()-t;
    cout << ans << endl;
    cout << (1.0*t)/CLOCKS_PER_SEC << " seconds." << endl;
    return 0;
}

随想

做题的时候没有想出来,有思路但是不完善,主要体现在for循环里的count计数上,明白为什么要计这个数很关键。考试的时候,脑子一团浆糊,效率很低。果然出去走走,再回来思考会更清晰。

第一遍尝试暴力地一个一个去检查n+1个数,超出时间限制(1s)。随后经实验知道,即使一个for循环什么都不干,让i白白自增一亿次,也是要5、6秒的时间的,因此遍历所有的数一定不可行。

上面代码中dp数组的含义为:dp[i] = i+1个二进制位表示的所有数中,满足条件的个数。例如,两个二进制位有4个数满足,三个二进制位有7个数满足, 四个二进制位有13个数满足。

也可以不计算len ,取31应该也行。

count 计数起截断作用,当碰到连续三个1时,就不用管后面有没有1了。代码的运算过程举个例子如下, 取n=21(10101)
从0到10101由00000~0111110000~1001110100~10100以及10101本身(这是为什么else里的ans要比if里的多加一)组成, 每组里符合条件的数的个数分别对应4位二进制数、2位二进制数、0位二进制数满足条件的数的个数(我应该修改一下dp数组,让它第一个数以1开始的,太晚了,不弄了)。这道题的难点就在怎么产生这个分组了,以及特别要注意碰到三个连续的1就应该截断了,例如10 0011 1010的分组应该为:00 0000 0000~01 1111 111110 0000 0000~10 0001 111110 0010 0000~10 0010 111110 0011 0000~10 0011 0111, 因为连续的三个1就不满足条件了,分组中止,比10 0011 0111大而比10 0011 1010小的满足条件的数不存在。分组的思想正是由于计算整k位二进制数符合条件的个数 好算(也就是dp数组好算),而把[0,n]分割成整的好算的区间。

表达能力有限,尽量看代码。

你可能感兴趣的:(面试编程题)