题目:
在[1, 2000000000]中给出整数S和整数E(S不大于E),求[S,E]中到整数转换成二进制后0个数大于等于1个数的数量。
思路:
刚开始以为是动态规划,也尝试去推到,没推出来,后来考虑除了首位是1后面到位数可以用0和1排列,只要后面到位数中0的个数大于1的个数就可以里。然后开始初始化杨辉三角。然后打表出二进制数长度从0到n有多少个满足条件的。最后剩下当前数字长度的可以使用循环查看每个位置到字符,如果是1,则将它变成0后继续排列组合从剩余位中取0,以此类推可以求出比num小的数中所有满足条件到。最终判断下num本身是否满足即可。
import java.io.*;
public class Main {
static int[][] c = new int[32][32];
static int[] dp = new int[31];
public static void main(String[] args) throws IOException {
init();
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
int T = (int) st.nval;
int s, e, ans;
for (int t = 1; t <= T; t++) {
st.nextToken();
s = (int) st.nval;
st.nextToken();
e = (int) st.nval;
ans = mm(e) - mm(s-1);
System.out.printf("#%d %d\n", t, ans);
}
}
static int mm(int num){
int cnt = 0;
char[] arr = Integer.toBinaryString(num).toCharArray();
int len = arr.length;
if (num > 1) cnt += dp[len-2];
int cnt0 = 0;
int m, n, mid = (len+1)>>1;
for (int i = 1; i < len; i++) {
if (arr[i] == '0')
cnt0++;
else {
m = max(0, mid-cnt0-1);
n = len - i - 1;
for (int j = m; j <= n; j++) {
cnt += c[n][j];
}
}
}
if (cnt0 >= mid) {
cnt++;
}
return cnt;
}
static void init() {
for (int i = 0; i < 32; i++) {
c[i][0] = c[i][i] = 1;
for (int j = 1; j < i; j++) {
c[i][j] = c[i-1][j-1] + c[i-1][j];
}
}
dp[0] = 0;
for (int i = 1; i < 31; i++) {
dp[i] = dp[i-1] + getLevelCnt(i);
}
}
static int getLevelCnt(int lv) {
int cnt = 0;
for (int i = (lv>>1) +1; i <= lv; i++) {
cnt += c[lv][i];
}
return cnt;
}
static int max(int a, int b) {
return a > b ? a : b;
}
}