こういう貪欲に突き進む処理を書くの、実はかなり苦手かもしれない
問題概要
個の 0 以上の整数 が与えられる。上手に正の整数 を選ぶことで、
- XOR
- XOR
- ...
- XOR
の値の最大値の最小値を求めよ。
制約
考えたこと
最小値を求めたいということで、一番上の桁から順に辞書順に決めていく。まず一番上の桁 桁目について、
- 個の整数について、 桁目がすべて 1 なら、丸ごとすべて 0 にできる
- すべて 0 なら、そのままで丸ごとすべて 0 にできる
ということがわかる。そうでない場合には、
- 0 の組を 1 にする代わりに、1 の組は 0 にする (このとき、1 の組のことはケアする必要がなくなる)
- 1 の組を 1 にする代わりに、0 の組は 0 にする (このとき、0 の組のことはケアする必要がなくなる)
という二択がある。いずれの場合も、答え res にとりあえず (1<<d) を加算した上で、
- 「0 の組」のみに index を絞って次の桁へ進む場合
- 「1 の組」のみに index を絞って次の桁へ進む場合
を両方試して、小さい方を採用すれば OK。計算量は実のところ、再帰が分岐していったとしても、どの桁についても結局 個の整数を一個ずつなめていくことになるので、 となる ( は の最大値)。
#include <iostream> #include <vector> using namespace std; int N; vector<long long> a; // a のうち ids で表される index のものについて、d 桁目について考える long long rec(int d, const vector<int> &ids) { if (d < 0) return 0; // d 桁目が 1 のやつと 0 のやつ vector<int> one, zero; for (auto i : ids) { if (a[i] & (1LL<<d)) one.push_back(i); else zero.push_back(i); } // どちらかが空なら d 桁目を 0 にできる if (one.empty() || zero.empty()) return rec(d - 1, ids); // そうでないなら d 桁目は 1 にする代わりに次回以降を絞る else return (1LL<<d) + min(rec(d-1, one), rec(d-1, zero)); } int main() { cin >> N; a.resize(N); for (int i = 0; i < N; ++i) cin >> a[i]; vector<int> ids(N); for (int i = 0; i < N; ++i) ids[i] = i; cout << rec(50, ids) << endl; }