半分全列挙のはなし
最近Atcoderの青difficulty問題を解き切ったので、それに絡めて記事を書いていこうと思います。初回は半分全列挙についてです。半分全列挙はその名の通り半分だけ状態を列挙する方法です。これだけだと何のことかわからないと思うので、例題を用いて考えてみましょう。 例題 パッと問題文を見るとDPが思い浮かぶと思いますが、制約をよく見るとX<109なので難しいです。一方、N<32と非常に小さいことに注目しましょう。ただし、荷物のあるなしの全ての状態を考えると232~109となり、間に合いません。そこで、荷物を半分づつグループに分けてそれぞれグループ1、グループ2と呼ぶことにします。簡単のためN=2nするととそれぞれのグループについてそこから何個か選ぶ組み合わせは2nなので全て列挙することができます。まずグループ1の状態を固定しその大きさをw1とします。すると問題の条件を満たすためには、グループ2から取ってきた荷物の重さの合計がw2 = W-w1となる必要があります。これを満たす選び方の数はグループ2の状態を全て列挙しているので簡単に求めることができます。
from bisect import * N, X = map(int, input().split()) w = [int(input()) for _ in range(N)] weight1 = [] for s in range(1<<(N//2)): res = 0 for i in range(N//2): if s & 1<<i: res+=w[i] weight1.append(res) weight2 = [] n = N-N//2 for s in range(1<<n): res = 0 for i in range(n): if s & 1<<i: res+=w[N//2+i] weight2.append(res) weight1.sort() weight2.sort() ans = 0 for w in weight1: l = bisect_left(weight2, X-w) r = bisect_right(weight2, X-w) ans+=(r-l) print(ans)
N~36の時はだいたい半分全列挙だと思いましょう。 類題