AtCoder Beginner Contest 164 F - I hate Matrix Construction
問題
次の条件を満たす整数行列が構築可能なら1つ求める。
- 行のbitwise-or/bitwise-andが
- 列のbitwise-or/bitwise-andが
考察
bitwise-or/bitwise-andなので、桁ごとに独立して考えてよい。 桁毎に分けると、は のいずれか。 そのように考えると、各行/列の条件は以下の4つのいずれかである。()内は、本記事でのこれらの状態の省略記法である。
- bitwise-andが1なら、すべて黒である()
- bitwise-orが1なら、1つ以上が黒である()
- bitwise-orが0なら、すべて白である()
- bitwise-andが0なら、1つ以上が白である()
解法1
強い制約として、の行と列はすぐに決まる。 また、行列ともにも、その色を使うことにしてよい。 よって、考える必要があるのは行列でが交差する、下図の?マークの箇所である。
1 | 1 | 1 | x | |
---|---|---|---|---|
1 | 1 | ? | 0 | |
1 | ? | 0 | 0 | |
x | 0 | 0 | 0 |
2行目3列目の「行がで、列が」な場合に注目する。 この列以外で、「」の列が存在すれば、白で確定させていい。また、この行以外で「」の行が存在しても黒で確定させられる。そうでない場合を考える。このような場合、行列の状況は下図のようになる。
1 | x | |
---|---|---|
? | 0 |
ここで、である行の数を、である列の数をとする。
- のとき、すでに塗り終えている
- のとき、白になる行を作れないので実現できない( のときも同様)
- のとき、すべて白にしないとの列の制約を満たせないので実現できない(のときも同様)
- を仮定すると、(なんとでも実装できるが)行列のインデックスの和の偶奇でと市松模様に塗るとの制約を満たせる
実装1
解法2
twitterでみた、最大フローを使って解く方法。
行/列を頂点とみなし、任意の行から列に容量1の辺を張る。は、行の頂点と列の頂点に張った辺のフローが1なら黒く、0なら白く塗ることになる。
行/列の条件は、以下のように言い換えられる。
- 黒マスの個数
- 黒マスの個数
- 黒マスの個数
- 黒マスの個数
まず、黒マスの必要量(黒マスの個数の下限)を確保することを考える。
起点→行→列→終点
のようなパスについて、
起点→行
任意の行に、容量がその行の黒マスの必要量の辺行→列
任意の行と列の組み合わせに容量1の辺列→終点
任意の列に、容量がその列の黒マスの必要量の辺
このグラフの起点から終点への最大フローは、行の黒マスの必要量の総和, 列の黒マスの必要量の総和になる。
行の黒マスが必要量に足りていないとき、列の余剰量(=最大量-必要量)を使って黒マスの必要量を塗ることができる。
ここで、先ほどのグラフに、次のような起点→行→列→終点
のパスを追加する。
起点→行
さっきのあまり行→列
さっきのあまり列→終点
任意の列に、容量がその列の黒マスの余剰量の辺
新たな起点から終点への最大フローは、行の必要量に足りなかった分を列の余剰量で満たす、ようになる。行と列を入れ替えて同じ処理を行うと、行/列それぞれの必要量を他方の余剰でまかなうことができた。
以上の処理で、可能な範囲で必要最低限の黒マスの塗り終わっている。
行必要量→列必要量
と行必要量→列余剰量
での最大フローの和が行の必要量の総和と等しければ、行は十分塗り終わっている。そうでない場合は、必要なのに塗れていな行があるため、実現不可能。列についても同様。
最大フローはDinic法を用いて求められる。
実装2
感想
実装1は数独とかヤジリンみたいな推論ゲームで、落ち着いてやれば難しくはないけど、コンテスト時間内で正確にやるのは結構しんどそう
実装2みたいな問題の置き換えができると、場合分けが少なくなってミスが減らせそう。