この記事は競プロ Advent Calendar 2022 11日目 のために作成されました。
燃やす埋める問題として解ける制約やその他関連情報をまとめました。
他の情報も見つけ次第更新していきます。(最終更新日:2022/12/11)
燃やす埋める問題とは
燃やす埋める問題[1]の元ネタは多分こんな感じです。「燃やす/埋める」以外にも「白で塗る/黒で塗る」、「使う/使わない」などの選択肢の問題もあります。
番目のゴミを燃やすときの損失が、埋めるときの損失が発生する。さらに、番目のゴミを燃やし番目のゴミを埋めると追加での損失が発生する。
すべてのゴミを適切に処理することで損失を最小化せよ。
この制約はグラフカット[2]に帰着され[3]、最小カット(=最大流)が答えとなります。
具体的には、以下のようなグラフを作り、-カットのうち最小のものを求めればよいです。のとき、ゴミを燃やすことに対応し、のとき、ゴミを埋めることに対応しています。
カットエッジは赤い頂点から青い頂点への有向辺であり、最小カットは各頂点を赤or青に塗り分けるもののうちカットエッジの重みの和を最小にするようなものになります。この塗り分けが燃やすor埋めるへの割り当てと同じ意味になっています。
この「燃やす埋める」というのは日本の競プロ界隈でしか使われていません。
流行りのChatGPTも知らないみたいです()
「Project Selection Problem」と言う人もいますが、燃やす埋めると問題設定が違うと思います(解ける問題の集合は同じ)[4][5]。
単に「最小カット」としか言ってないような解説も多いです。
燃やす埋める問題として解ける制約
燃やす埋める問題として解ける制約について解説します。他にもあると思いますが分かる範囲で書いてます。
ゴミを燃やしたときの損失
のとき
ゴミを燃やしたときの損失発生するので、のときにの重みのカットエッジがあるようにグラフを作ればよいです。そのようなグラフは頂点から頂点に重みの有向辺を張ることで作ることができます。
のとき
ゴミを燃やすとの損失無条件での損失+ゴミを埋めるとの損失と言い換えることができるので、次に説明する埋めたときと同様に辺を張ることができます。
例
ゴミを埋めるとの損失が発生する場合、以下のように辺を張ったグラフの最小カットの重みを求め、それにを加えたものが問題の答えとなります。
ゴミを埋めたときの損失
のとき
のときカットの重みがである頂点から頂点に重みの有向辺がある。のとき
ゴミを燃やしたとき、の損失が発生する場合と同様に考えることができます。ゴミを燃やしゴミを埋めたときの損失
かつのときカットの重みがである頂点から頂点に重みの有向辺がある。つのゴミの処理方法による損失
実は[ゴミiを燃やしゴミjを埋めたときCij(> 0)の損失]は一般化できます。ゴミとゴミの処理方法(燃やす、埋める)による損失が以下のように表されるとします。
この損失がを満たす場合、以下のように分解することができます。
無条件にの損失
+ゴミを埋めるとの損失
+ゴミを埋めるとの損失
+ゴミを燃やし、ゴミを埋めるとの損失
これらはすでに説明した方法でグラフを作ることができます[6][7]。
ゴミの処理方法による損失
つのゴミの処理方法による損失を考えます。詳しい説明(理由)は他の方の記事[6][7]に任せますが、この損失を表現するには「ゴミのうち、つ以上が燃やされたときの損失が発生する」という損失を表現する必要があります。の場合について説明します。
これは、新たにグラフに「ゴミのうち、つ以上が燃やされた(※)」ことを表す頂点を追加し、(※)が真の場合、偽の場合になると考えると、
(※)が偽かつゴミを燃やすとの損失
+(※)が偽かつゴミを燃やすとの損失
+(※)が偽かつゴミを燃やすとの損失
+(※)が真ならの損失
として表すことができます。
複数のゴミのうちつ以上が燃やされたときの損失
ゴミの集合に含まれるゴミのうち、つ以上が燃やされたときの損失がかかることをグラフで表現します。先程つのゴミの場合を説明しましたが、これと同様の方法で表現することができます。
複数のゴミのうちつ以上が埋められたときの損失
ゴミの集合に含まれるゴミのうち、つ以上が埋められたときの損失がかかることをグラフで表現します。先程と同様に考えると以下のように表現することができます。
より複雑な損失
- ゴミの集合と、
- 、
が与えられたときに、
- に含まれるゴミのうち燃やしたゴミににおけるの和
- に含まれるゴミのうち埋めたゴミにおけるの和
の最小値が損失となるようなグラフを作ります。
詳細は省略しますが、以下のように新たに頂点を追加し、有向辺を張ればよいです。
これが正しく損失を表現できていることは、任意の-カットにおいて、このカットエッジの重みが最小となるの割り当てを考えることで確認出来ます。
図ではに見えますが、であっても構いません。
(のの辺は張らなくてもいいかも...)
また、
- は
- は
- は
とすることで表現できます。
したがって、上で説明した以下のつはこの損失の特殊な例と考えることができます。
複数のゴミのうち1つ以上が燃やされたときC(> 0)の損失
複数のゴミのうち1つ以上が埋められたときC(> 0)の損失
他にも、以下のようにこの構造を複数組み合わせるとより複雑な損失を表現することができます。複雑すぎる割にあまりコンテストに出てこなさそうなのでこの記事では説明しませんが、どのような損失を表すのか考えてみてください。
3つ以上の処理方法がある場合
これまでは選択肢がつ([燃やす/埋める])の場合について説明してきました。以下のグラフにおいて、頂点を赤に塗る()ことが[燃やす]を選ぶことに対応し、青に塗る()ことが[埋める]を選ぶことに対応しています。これを選択肢がつ以上ある場合に拡張します。
ゴミの処理方法として個の選択肢([処理/処理//処理])があるとします。
以下の条件を満たす頂点を新たに追加します。
- のとき、[処理, 処理, , 処理]のうちいずれも選ばない。
- のとき、[処理, 処理, , 処理]のうちいずれかを選ぶ。
となることはないことに注意すると、以下のようなグラフを作ることができます。は処理を選んだ時の損失です。
が負の場合でも、無条件での損失を発生させ、とすることでとすることができます。
他にも[8]のように張る方法もあります。
2つのゴミの処理方法による損失
つのゴミの処理方法の選択による損失を考えます。以下のように- 赤:からへの有向辺
- 橙:からへの有向辺
- 緑:からへの有向辺
- 青:からへの有向辺
が張られたグラフがどのような損失を表しているかを考えます。
損失を表で書くと以下のようになります。辺の色と同じ色の枠の部分には一律に辺の重み分の損失が発生します。
損失をこの形に分解することができればグラフで表すこともできるということになります。例として以下のような損失をグラフで表してみます。
ゴミiを燃やしたときAiの損失やゴミiを埋めたときBiの損失を用いると、行や列から一律に値を引くことができます。
まず、ゴミに対して[処理]を選ぶとの損失が発生することにすると、ゴミの[処理]の行からを引くことができます。同様に[処理/処理]の行についても行います。
ゴミに対して[処理]を選ぶとの損失(の利得)が発生することにすると、ゴミの[処理]の列からを引くことができます。同様に[処理/処理]の列についても行います。
先程の色枠の表を見ると、損失が次元累積和のようになっており、差分を取ることでそれぞれの辺の重みを求めることができます。
このとき、差分を取った値(右表)は非負である必要があります。
非負である条件として、損失の表(左表)内のすべてのの部分を抜き出し、値を以下のようにとしたときであることが必要です。
∞の損失の扱い
これで機械的に解けるようになったわけですが、の損失を扱う場合には注意が必要です。ここで以下のような損失を考えてみましょう。実装の際、を巨大な定数として扱ってしまうと非負条件()が成り立たないためグラフを作ることができません。
しかし、実際には以下のようなグラフによりこの損失を表現することができます。
※これの処理方法について書きたいのですが投稿日に間に合わなさそうだったので後日書きます。
処理方法の決め方
各ゴミの処理方法は自由に決めることができます。[8]ゴミは(処理,処理)=(燃やす,埋める)、ゴミは(処理,処理)=(埋める,燃やす)といったように、各ゴミで処理方法の並びが異なっていても構いません。
これを利用すると以下のような非負条件()を満たさない場合にも[2つのゴミの処理方法による損失]で説明した方法でグラフを作ることができる場合もあります。
部グラフ系の問題でこの変換を使うことがあります。
その他の損失
その他の損失を問題形式でいくつか置いておきます。重要な問題も含まれているので是非解いてみてください。ゴミとゴミを燃やすとの利得(の損失)が発生する。
ゴミの集合に含まれるゴミのうち、燃やされたゴミの数をとする。このとき、の利得が発生する。
解説
に含まれるすべてのゴミのペアについて、ゴミとゴミを燃やすとの利得が発生するようにします(損失問題)。
個のゴミが燃やされたとするとの利得が発生しますが、さらにについてゴミを燃やした時にの利得が発生するものとすれば、追加での利得が発生し、全体での利得を表現するとこができます。
一般に、ゴミの集合およびが与えられているとき、となるので、は「を燃やすとの利得」、「を燃やすとの利得」によって表現できます。
ゴミの集合に含まれるゴミのうち、燃やされたゴミの数をとする。このとき、の利得が発生する。
解説
損失問題と同様に考えると3つのゴミの処理方法による損失が必要になってしまい、の頂点や辺が追加されてしまいます。これを頂点、辺に抑える方法を説明します。
実は、損失の関数においてが成り立つならばグラフで表現することができます。今回の問題ではなのでこれが成り立っています。
例として、の以下のような関数を考えます(分かりやすいように折れ線で表しています)。
結論から言うと、この関数は、
と分解できます[9]。一般に、この条件を満たす関数は正整数を用いてやと表されるものの和として表すことができ、これはまさに[より複雑な損失]で説明したようにグラフで表現することができます。
答えの復元
最小カットの重みで求めた後、それぞれのゴミの処理としてどれを選んだかを求めることができます。[1]で説明されているように、各頂点がであるかであるかは、が残余ネットワークにおいて始点から到達可能であるかどうかで分かります。
計算量
最大フロー最小カット定理[10]より、最小カットを求めるには最大フローを求めればよいです。頂点辺のグラフを考えます。
Dinic法で解く場合、計算量はとなりますが、様々な条件下でより計算量が落ちることがあります[11]。
他にも最大フローを求めるアルゴリズムとして
などがあります。
ライブラリの作成
これまでの説明をもとに燃やす埋める問題を解くライブラリを作る場合、以下のような機能があればいいと思います。ゴミの処理方法の数(必須)
各ゴミの処理方法の数を与えます。何よりも先に必要です。ゴミの処理方法による損失(必須)
各ゴミの処理方法による損失を与えるとその損失に応じた辺を張ります。負の数でも構いません。ただし、損失が与えられた時点で辺を張るのではなく最小カットを求める直前に辺を張るようにしてください。後程説明する他の種類の損失での値が変更される場合があるためです。
つのゴミの処理方法による損失(必須)
つのゴミの処理方法の選択間にかかる損失を与えるとその損失に応じた辺を張ります。[2つのゴミの処理方法による損失]で説明した非負の条件を満たすことを関数内で確認するようにしましょう。
損失が与えられた時点で辺を張ってもいいですが、後から変更される場合もあるので注意してください。
答えの復元
ここで説明したとおりに最小カットの復元を行いましょう。頂点かつであるならば処理を選んだことになります。
複数のゴミのうちつ以上が処理をされたときの損失
たまにこの制約がある問題を見ます。任意のでならコレとコレで実現できます。
他にも「複数のゴミのうちすべて処理をしたらの利得」に言い換えることもできます。
の場合にも拡張できますが、変な制約になります(素直に「つ以上が処理をしたらの損失」みたいにはなりません)。
つのゴミの処理方法における損失
[3つ以上の処理方法がある場合]で説明したものです。数回だけこれが必要な問題を見ました。
その他の損失
損失問題2はどこかで見たような記憶がある程度。[より複雑な損失]や損失問題3の損失は見たことないです。
流行り出したら作るくらいでいいでしょう。
練習問題
長さの整数列を作ろうとしています。各について,の値の候補が種類あり、そのうち種類目の値はです。なお、を選ぶ場合には、のコストがかかります。また、を決めたあと、各 について、 のコストが追加でかかります。 最終的なコストの総和としてありうる最小値を求めてください。
のマス目が与えられます。既に白か黒で塗られているマスがあり、その他のマスは塗られていません。色が塗られていないマスをすべて白か黒で塗り、マス目上のチェックパターンの数を最大にしたいです。の正方形を形成するつのマスは、以下のいずれかのように塗られている場合、チェックパターンであると言います。
解説
各マスの選択肢は[白で塗る/黒で塗る]です。既に塗られたマスを異なる色で塗ろうとする選択にはの損失が発生するようにします。
すべてのの正方形に対して、チェックパターンならの利得が発生するようにしたいですが、このままではうまくできません。
ここで、となるマスの選択肢を[黒で塗る/白で塗る]に変更すると、チェックパターンの制約が「の正方形を形成するつのマスすべてが選択肢/選択肢を選ぶ」と言い換えられるのでコレやコレが使えます。
他の問題は以下の記事にまとめています。
kanpurin.hatenablog.com