【数位 dp】不含连续1的非负整数(逆向构造 / dp)

一、Problem

给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含 连续的1 的个数。

输入: 5
输出: 5
解释: 
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则

说明: 1 <= n <= 109

二、Solution

方法一:逆向构造(超时)

思路

  • 从右到左开始添加二进制位:
    • 如果第 i i i 位是 0,前面的第 i + 1 i+1 i+1 位可以是 0/1
    • 如果第 i i i 位是 1,前面的第 i + 1 i+1 i+1 位只能是 0
  • 返回时机:
    • 如果 cur > lim,表示超出范围,返回 0
    • 如果 cur <= lim && 再添加一位二进制位就会大于 lim 的话,则返回 1(因为 cur 有可能后 i − 1 i-1 i1 位全是 0,而恰好在第 i i i 位添加 1 就 > lim,故要考虑到这种极端情况)

TLE 之 381/527

class Solution {
public:
	int lim;
	int dfs(int i, int cur, int last) {	//i数字的位数;cur当前数字;last上一位数字
		if (cur > lim) return 0;
		if (1<<i > lim)return 1;
		if (last == 1) return dfs(i+1, cur, 0);
		return dfs(i+1, cur+(1<<i), 1) + dfs(i+1, cur, 0);
	}
    int findIntegers(int num) {
    	lim = num;
    	return dfs(0, 0, 0);
    }
};

复杂度分析

  • 时间复杂度: O ( . . . ) O(...) O(...)
  • 空间复杂度: O ( 1 ) O(1) O(1)

方法二:数位 dp

思路

  • 定义状态
    • f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1] 表示第 i i i 位为 0/1 的合法数字个数
  • 思考初始化:
    • f [ 1 ] [ 0 ] = f [ 1 ] [ 1 ] = 1 f[1][0] = f[1][1] =1 f[1][0]=f[1][1]=1
  • 思考状态转移方程
    • f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] f[i][0] = f[i-1][0] + f[i-1][1] f[i][0]=f[i1][0]+f[i1][1]
    • f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] f[i][1] = f[i-1][0] f[i][1]=f[i1][0]
  • 思考输出 . . . ... ...

状态定义和状态转移都比较简单,重点是如何统计合法数字的数目,这里我使用的做差法,先统计所有合法的数字个数,在减去重复统计的数目

比如

class Solution {
public:
    int findIntegers(int num) {
        int n=0, b[33], f[33][2] = {0};
    	while (num) {
    		b[n++]=num&1;
    		num>>=1;
    	}
    	f[0][0]=f[0][1]=1;
    	for (int i = 1; i < n; i++) {
    		f[i][0] = f[i-1][0]+f[i-1][1];
    		f[i][1] = f[i-1][0];
    	}
    	
    	int ans=f[n-1][0]+f[n-1][1];
    	for (int i = n-2; i >= 0; i--) {
    		if (b[i] && b[i+1]) break;
    		if (!b[i] && !b[i+1]) ans-=f[i][1];
    	}
    	return ans;
    }
};

f[i][0/1] 都只与 f[i-1] 有关,可将 f 压缩到 1 维

复杂度分析

  • 时间复杂度: O ( l e n ( n u m ) ) O(len(num)) O(len(num))
  • 空间复杂度: O ( l e n ( n u m ) ) O(len(num)) O(len(num))

你可能感兴趣的:(#,数位,dp)