POJ 3252 Round Numbers

题目链接:POJ 3252 Round Numbers

题目大意:
\([a,b]\)内转换成二进制之后\(0\)的个数大于等于\(1\)的个数的数的数量。

题解:
\(dp[i][j]\)表示当前为第\(i\)位,\(0\)的个数与\(1\)的个数的差值为\(j\)的数的数量,因为差值可能为负,所以让它整体加\(32\)防止出现负数。
之后就是数位dp和记忆化搜索,参数中\(num\)表示\(0\)\(1\)的个数差,\(pre\)表示高位是否出现过\(1\),只有高位出现过\(1\),此时的\(0\)才能计数。

#include <iostream>
#include <cstring>
using namespace std;

int dp[35][70], digit[70];

int dfs(int pos, int num, bool pre, bool limit) {
// num:0和1的个数差,由于要记忆化搜索,所以整体加32防止出现负数
// pre:高位是否有1出现过
    if (pos <= 0) return num >= 32;
    if (pre && !limit && dp[pos][num] != -1) return dp[pos][num]; // 没限制则用记忆化搜索
    int up = limit ? digit[pos] : 1;
    int ans = 0;
    for (int i = 0; i <= up; ++i) {
        if (!pre && i == 0) // 高位没有1且这一位也不是1
            ans += dfs(pos - 1, num, pre, limit && i == digit[pos]);
        else // 高位有1或者这一位是1
            ans += dfs(pos - 1, num + (i == 0 ? 1 : -1), pre || i == 1, limit && i == digit[pos]);
    }
    if (!limit && pre) dp[pos][num] = ans;
    return ans;
}

int cal(int x) {
    int cnt = 0;
    while (x) { // 存放二进制
        digit[++cnt] = x & 1;
        x >>= 1;
    }
    return cnt;
}

int main() {
    int a, b;
    cin >> a >> b;
    memset(dp, -1, sizeof(dp));
    cout << dfs(cal(b), 32, false, true) - dfs(cal(a - 1), 32, false, true) << endl;
    return 0;
}
posted @ 2021-01-20 20:35  ZZHHOOUU  阅读(43)  评论(0编辑  收藏  举报