ABC432 F - Candy Redistribution で知ったテク
この問題は最終的に以下の問題を解くことに帰着されるのですが、解法が面白かったのでメモ。
問題
和が 0 であるような 個の数がある。これらを、和が 0 のグループに分割する。グループ数の最大値を求めよ。
例: [1,2,4,-1,-1,-2,-3] → [[1,2,-3], [4, -1, -1, -2]]、[[1,-1], [2,-2], [4,-1,-3]] などの分割方法がある。グループ数の最大値は 3。
解法
以下のような のアルゴリズムはすぐわかります。
vector<int> dp(1 << N, -1);
dp[0] = 0;
for (int bit = 0; bit < (1 << N); bit++) {
for (int sub = bit; sub; sub = (sub - 1) & bit) {
if (sum[sub] == 0) chmax(dp[bit], dp[bit ^ sub] + 1);
}
}
しかし、 が最大 20 なので では厳しく、 で求める必要があります。
ポイントは、「和が 0 のグループ数の最大値」を「数を任意に並べ替えたときの、累積和が 0 になる回数の最大値」と対応させられることです。これを利用することで、以下の の DP が得られます。
vector<int> dp(1 << N);
for (int bit = 0; bit < (1 << N); bit++) {
for (int i = 0; i < N; i++) {
if (bit & (1 << i)) {
chmax(dp[bit], dp[bit ^ (1 << i)] + (sum[bit] == 0 ? 1 : 0));
}
}
}
公式解説はこの問題に帰着するところまでの説明がメインで、ここは「1 要素ずつ追加していく bitDP によって 時間で計算できます」と書かれているのみだったのですが、自分はこのパートだけわかりませんでした🙁
有名なのかもしれないですが、知らなかった & 面白かったです。