京都大学プログラミングコンテスト2015 H - Bit Count

,

kupc楽しかったです。京都オンサイトで解きました。7完できて34位だったのかなり嬉しいです。ありがとうございました。


残り1分切ってから通した。ぎりぎり。

H - Bit Count

問題

正整数Nが与えられる。XとX+Nの2進表記中の1の数が等しくなるような正整数Xで最小のものを求めよ。

解法

最下位桁から再帰的にXを決めていく。 Xを決めたことにより変化したNと、決定済みのX及び確定済みのN+Xの中の1の数の差でメモ化する。 Nの最下位桁が0である場合は適当にshiftしてよい。

解答

#include <iostream>
#include <map>
#include <utility>
#define repeat_from(i,m,n) for (int i = (m); (i) < (n); ++(i))
#define repeat(i,n) repeat_from(i,0,n)
typedef long long ll;
using namespace std;
constexpr ll inf = 10000000000000000ll;
ll solve(ll n, int used, int limit, map<pair<ll,int>,ll> & memo) {
    auto key = make_pair(n, used);
    if (memo.count(key)) return memo[key];
    if (limit < used) return inf;
    if (used == __builtin_popcountll(n)) return 0;
    if (n == 0) return inf;
    int shift = 0;
    while (not (n & 1)) { n >>= 1; shift += 1; }
    ll x = inf;
    x = min(x, solve(n >> 1, used - 1, limit, memo) << 1);
    x = min(x, (solve((n + 1) >> 1, used + 1, limit, memo) << 1) + 1);
    if (x != inf) x <<= shift;
    return memo[key] = x;
}
int main() {
    int datasets; cin >> datasets;
    repeat (dataset, datasets) {
        ll n; cin >> n;
        map<pair<ll,int>,ll> memo;
        cout << solve(n, 0, __builtin_popcountll(n), memo) << endl;
    }
    return 0;
}