- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
java解法
- 解题思路
- 问题描述
给定两个整数区间 [l, r],要求计算在此区间内,所有数字的 二进制表示中 不包含 连续的 “101” 的数字个数。
方法
这是一个经典的 数字DP 问题:
状态表示:用一个数组 dp 表示从当前位置到结束位置,符合条件的二进制数的个数。
条件限制:不能出现连续的 “101”。
转移过程:
当前位可以取 0 或 1,但需要满足前两位的状态(用 p1 和 p2 表示前两位)不构成 “101”。
若当前位置取值后导致不满足条件,则跳过。
二进制转换:将给定数字转换成二进制表示,从高位到低位进行DP遍历。
关键细节
lim 参数表示当前位是否受到数字大小的限制。
使用 p1 和 p2 表示当前位的前两位的取值。
通过记忆化搜索(dp 数组)减少重复计算。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int l = in.nextInt();
int r = in.nextInt();
int res = find(r) - find(l - 1);
System.out.println(res);
}
public static int find(int num) {
Integer[] binArr = Arrays.stream(Integer.toBinaryString(num).split(""))
.map(Integer::parseInt)
.toArray(Integer[]::new);
int[][][] dp = new int[binArr.length][2][2];
return dfs(0, true, dp, binArr, 0, 0);
}
public static int dfs(int pos, boolean lim, int[][][] dp, Integer[] binArr, int p1, int p2) {
if (pos == binArr.length) return 1;
if (!lim && dp[pos][p1][p2] != 0) return dp[pos][p1][p2];
int max = lim ? binArr[pos] : 1;
int cnt = 0;
cnt += calcRec(max, pos, lim, dp, binArr, p1, p2);
if (!lim) dp[pos][p1][p2] = cnt;
return cnt;
}
public static int calcRec(int max, int pos, boolean lim, int[][][] dp, Integer[] binArr, int p1, int p2) {
int sum = 0;
for (int i = 0; i <= max; i++) {
if (i != 1 || p1 != 0 || p2 != 1) {
sum += dfs(pos + 1, lim && i == max, dp, binArr, i, p1);
}
}
return sum;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
C++解法
- 解题思路
- 问题描述
给定一个整数区间 [l, r],计算该区间内所有数字的 二进制表示 中不包含 连续 “101” 的数字个数。
方法:数字DP
将数字转换为二进制数组:将区间的上界数字转换成二进制数组,从高位到低位逐位处理。
递归枚举每一位的取值:
使用递归深度优先搜索(DFS),逐位选择二进制值(0 或 1)。
当前位的最大取值由是否受限(limited)决定:
如果当前位受限,则取值范围是 0 到该位的实际值。
如果不受限,则取值范围是 0 到 1。
状态转移条件:
如果当前选择形成了 “101” 模式,则跳过该选择。
其他情况下继续递归,并更新状态。
记忆化搜索:
使用 memo 数组记录已经计算过的状态,避免重复计算。
最终结果:分别计算 [1, r] 和 [1, l-1] 范围内符合条件的数字个数,二者相减即为结果。
#include
#include
#include
#include
using namespace std;
int countBinary(int p, bool limited, int memo[][2][2], vector<int>& bits, int prev, int prevprev) {
if (p == bits.size()) {
return 1;
}
if (!limited && memo[p][prev][prevprev] != -1) {
return memo[p][prev][prevprev];
}
int bound = limited ? bits[p] : 1;
int total = 0;
for (int bit = 0; bit <= bound; bit++) {
if (bit == 1 && prev == 0 && prevprev == 1) continue;
total += countBinary(p + 1, limited && bit == bound, memo, bits, bit, prev);
}
if (!limited) {
memo[p][prev][prevprev] = total;
}
return total;
}
int binaryRange(int num) {
vector<int> bits;
while (num) {
bits.push_back(num % 2);
num /= 2;
}
reverse(bits.begin(), bits.end());
int memo[32][2][2];
memset(memo, -1, sizeof(memo));
return countBinary(0, true, memo, bits, 0, 0);
}
int main() {
int l, r;
cin >> l >> r;
cout << binaryRange(r) - binaryRange(l - 1) << endl;
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
C解法
更新中
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
JS解法
解决方法
使用数字DP方法,通过逐位处理二进制数字,动态计算符合条件的数字个数。
二进制转换:将数字转为二进制数组,逐位处理。
递归搜索(search 函数):
遍历每一位的可能取值(0 或 1)。
如果当前选择形成了 “101”,则跳过。
递归地处理下一位,同时更新前两位的状态(last 和 secondLast)。
状态记忆化:
使用 dp 数组存储已经计算过的状态,避免重复计算。
状态由三部分组成:
当前处理的位置 pos。
前一位的值 last。
前两位的值 secondLast。
最终结果:分别计算 [1, R] 和 [1, L-1] 的符合条件的数字个数,二者相减即为答案。
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
const [L, R] = (await readline()).split(" ").map(Number);
const result = countValidNumbers(R) - countValidNumbers(L - 1);
console.log(result);
})();
function countValidNumbers(num) {
const binaryArr = num.toString(2).split("").map(Number);
const dp = new Array(binaryArr.length)
.fill(0)
.map(() => new Array(2).fill(0).map(() => new Array(2)));
return search(0, true, dp, binaryArr, 0, 0);
}
function search(pos, isLimit, dp, binaryArr, last, secondLast) {
if (pos === binaryArr.length) return 1;
if (!isLimit && dp[pos][last][secondLast]) return dp[pos][last][secondLast];
const maxDigit = isLimit ? binaryArr[pos] : 1;
let totalCount = 0;
for (let digit = 0; digit <= maxDigit; digit++) {
if (digit === 1 && last === 0 && secondLast === 1) continue;
totalCount += search(
pos + 1,
isLimit && digit === maxDigit,
dp,
binaryArr,
digit,
last
);
}
if (!isLimit) dp[pos][last][secondLast] = totalCount;
return totalCount;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
注意:
如果发现代码有用例覆盖不到的情况,欢迎反馈!会在第一时间修正,更新。
解题不易,如对您有帮助,欢迎点赞/收藏
评论记录:
回复评论: