ABC166F を直感的なDFSで
直感的な、と書いていますが、計算が間に合うことは直感に反すると思います。
問題概要
非負整数の値をとる変数 に対して、操作を 回行います。 番目の操作は、長さ の文字列 によって表され、指定された 変数のうち一方を 増やし、他方を 減らすというものです。すべての操作が可能かどうか判定してください。さらに、可能なら手順をひとつ提示してください。
解答
最も直感的な解法は、操作ごとに二通りに分岐して、すべてのパターンを試すことでしょう。手順の提示を省略し、疑似コードで示します。
procedure dfs( i ) = if i > N then 成功 foreach S_i の意味する操作 do if 操作が可能 then 操作 dfs( i + 1 ) 逆操作 dfs( 1 )
これでは間に合うわけがありません。 個もの状態を考慮することになります。
……というのは嘘で、これで十分間に合います。以下にざっくり示します。
正当性(なぜ速い?)
のとき、明らかに即座に終了します。 のとき、dfs(i)
は高々 回しか dfs(i+1)
を呼び出さないため、このDFSは時間計算量 で動作します。以下、 を仮定します。
操作ができないとき、すなわち dfs(i)
が dfs( i+1 )
を呼ばないとき1を考えます。たとえば操作 'AB'
が与えられたときに、 だったとします。
- ひとつ前の状態がないとき、操作は不可能です。
- ひとつ前の状態があるとき、ひとつ前の操作は確実に
'AC'
か 'BC'
であり、確実に か を 減らしています。- ひとつ前の操作で を減らすほうを選べる場合、そちらを選べば操作
'AB'
は確実に可能です。 - ひとつ前の操作で を減らすほうを選べない場合、ひとつ前の状態は のどちらかです。 なのでありえません。
- ひとつ前の操作で を減らすほうを選べる場合、そちらを選べば操作
これをもとに考えると、各 i
について dfs(i)
が高々 回しか呼ばれないことがわかります2。よってこのDFSは時間計算量 で動作します。