数位DP问题
数位DP问题一般都是问在 小于等于 n 的数里面, 有多少个数是满足条件的, 当然问题也可以转换为问一个区间里面[a, b] 是满足某种条件的, 当然对于这种区间的问题, 我们可以将其转化为前缀和问题, 例如转化为求 0 ~ b里满足的个数 - 0 ~ a - 1 里面满足条件的个数, 一般做法分两步:
(1) 预处理 (一般情况)
(2) 按位进行处理
例题
题目链接: 600. 不含连续1的非负整数
题目描述:
给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含 连续的1 的个数。
示例:
输入: 5
输出: 5
解释:
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。
说明: 1 ≤ \le ≤ n ≤ \le ≤ 1 0 9 10^9 109
思路分析
首先思考暴力做法, 即从 n 开始一直到 0, 判断每一个数是否满足条件, 这里判断可以利用位操作在 O ( 1 ) O(1) O(1)的时间内判断出来, 但是数量级 1 0 9 10^9 109 必然超时
// 超时的暴力做法
class Solution
{
public:
void test(int n)
{
cout << n << " ";
for(int i = 31; ~i; --i) cout << (n >> i & 1);
cout << endl;
}
bool fun(int n)
{
int num = n;
while(n)
{
int t = n & -n;
n -= t;
if(n)
{
int t2 = n & -n;
if(t2 == t * 2) return true;
}
}
return false;
}
int findIntegers(int n)
{
int num = n;
int res = 0;
while(~num) res += fun(num--);
return n + 1 - res;
}
};
数位DP分析:
上面例子中我们假设给我们一个非负整数n其二进制表示为 10100110, 合法的数字的规则是小于等于n的数, 且这个数的二进制表示中不能有连续的1(具体见示例), 故我们这里从严格小于n的数开始找起, n这个数本身我们可以最后判断, 首先这里n的最高位为1, 那么此时的位数总共为8位,那么此时二进制表示为8位且最高位为0的数必定是小于n的数, 其中符合条件的数, 我们从预处理条件可以知道为 f(8, 0); 接着假定此时最高为1, 接着判断第7位, 由于此时第八位已经为1, 如果要合法那么第七位只能为0, 接着判断第六位, 此时如果为0, 那么合法个数为f(6, 0); 接着假定第六位为1,继续判断第五位的情况, 同样要保持合法, 第五位只能为0, 判断第四位,由于n的第四位已经为0了,不能再比其更小了, 故第四位为0的情况, 判断第三位, 假定第三位为0, 则合法个数为 f(3, 0), 假定第三位为1的情况下, 第二位假设为0, 此时合法个数为 f(2, 0), 此时第二位为1就不合法, 故如果原本的n中如果出现连续的1那么后面就可以不用判断了.
所以可以得出这里的状态转移方程:
注意这里的f(x, 0/1) 仅表示共 x 位的且最高位为0, 1的合法个数(不含连续1的个数)
f(i, 0) = f(i - 1, 0) + f(i - 1, 1); // 最高位为0, 所以次高为可以为 0 或者 1
f(i, 1) = f(i - 1, 0); // 最高位为1, 次高位仅能为0, 否则就构成连续的1
class Solution
{
public:
int f[40][2];
int findIntegers(int n)
{
memset(f, 0, sizeof f);
vector<int> num;
while(n) num.push_back(n % 2), n /= 2;
f[1][0] = f[1][1] = 1;
for(int i = 2; i <= num.size(); ++i)
{
f[i][0] = f[i - 1][0] + f[i - 1][1];
f[i][1] = f[i - 1][0];
}
int res = 0;
for(int i = num.size(), last = 0; i; --i)
{
int n = num[i - 1];
if(n)
{
res += f[i][0];
if(last == 1) return res; // 出现连续的1, 此时继续下去组成的必然是不合法的数字, 所以直接返回
}
last = n;
}
return res + 1;
}
};