# 2SAT




原案、問題文:宮村
解答:宮村、橋本
 解説:宮村
問題概要
●   2SAT が与えられる。
●   各変数の出現回数は 1 or 2  回。
●   True になる割り当てが何通りあるか求める。

●   各変数の出現回数について制限が無い場合、
     #P­complete であることが知られている。
     ( 効率的な解法はおそらく存在しない )
観察
●   とりあえずグラフで表現してみよう。
●   例: (x1∨~x2)∧(~x3∨x2)∧(~x3∨~x4) 




      x1        ~x3        ~x3




      ~x2        x2        ~x4
観察
●   ここで、「変数の出現回数は 1 or 2 回」
    に注目してみよう。
●   この条件から、各ノードの最大次数は 2 以下。
●   グラフは 線 or  環 が集まったものになる。
●   連結成分毎に考えて最後に掛け合わせれば良い。


                     次数 3
特別な場合



●   ひとつのブロックに同じ変数が入っていた場合 :
●   (x ∨ x) や (~x∨~x) のような場合は 1 を返す
●   (x ∨ ~x) のような場合は 2 を返す
線の場合
●   dp[i][j] := {i 番目のブロックで、 i+1 番目のブロック
      と隣接するノードの値は j であり、 1~i 番のブロッ
      クは全て true であるような割り当ての数 }
●   下の図では、赤い辺は 0, 1 が反転するようなペア
     で、黄の辺は 0, 1 が等しいようなペア

      1     2        3    4     5

     x1   ~x3   ~x3      x5   ~x5


     x2   ~x2   x4       x4   ~x6
線の場合
●   dp[1][1] = 2, dp[1][0] = 1
●   最終的な答は
      dp[N][0] + dp[N][1]
    となる。

        1      2         3    4     5

      x1     ~x3   ~x3       x5   ~x5


      x2     ~x2    x4       x4   ~x6
線の場合
●   ブロック i と i­1 が赤い辺でつながっているとき:
●   dp[i][0] = dp[i­1][0]
●   dp[i][1] = dp[i­1][1] + dp[i­1][0]



        1      2         3    4     5

      x1     ~x3    ~x3      x5   ~x5


      x2     ~x2    x4       x4   ~x6
線の場合
●   ブロック i と i­1 が黄の辺でつながっているとき:
●   dp[i][0] = dp[i­1][1]
●   dp[i][1] = dp[i­1][1] + dp[i­1][0]



        1      2         3    4     5

      x1     ~x3    ~x3      x5   ~x5


      x2     ~x2    x4       x4   ~x6
環の場合
●   環になっている場合は、どこかで切断して無理やり
     線の場合に落とす。
●   切ったときに、 1 番のブロックに入っているペアの
     片割れが 0 になるか 1 になるか両方試す。


      1     2        3    4     5

     x1   ~x3   ~x3      x5   ~x5


     x2   ~x2   x4       x4   x1
環の場合
●   切断した箇所が赤い辺の場合は以下の 2 つの和:
●   左端を 1 にした場合 : dp[1][1] = 1, dp[1][0] = 1 と
     して dp[N][0] を求める。
●   左端を 0 にした場合: dp[1][1] = 1, dp[1][0] = 0 と
     して dp[N][1] を求める。
       1     2        3    4     5

      x1   ~x3   ~x3      x5   ~x5


      x2   ~x2   x4       x4   ~x1
環の場合
●   切断した箇所が黄の辺の場合は以下の 2 つの和:
●   左端を 1 にした場合: dp[1][0] = 1, dp[1][1] = 1 と
     して dp[N][1] を求める。
●   左端を 0 にした場合: dp[1][0] = 0, dp[1][1] = 1 と
     して dp[N][0] を求める。
       1     2        3    4     5

      x1   ~x3   ~x3      x5   ~x5


      x2   ~x2   x4       x4   x1
計算量




●   グラフのビルド、 DP の計算ともに O(N) 。
解答例
●   宮村 (C++)
      127 行 2577 byte

●   橋本 (Java)
      161 行 4841 byte

Sharp2sat

  • 1.
  • 2.
    問題概要 ● 2SAT が与えられる。 ● 各変数の出現回数は 1 or 2  回。 ● True になる割り当てが何通りあるか求める。 ● 各変数の出現回数について制限が無い場合、  #P­complete であることが知られている。  ( 効率的な解法はおそらく存在しない )
  • 3.
    観察 ● とりあえずグラフで表現してみよう。 ● 例: (x1∨~x2)∧(~x3∨x2)∧(~x3∨~x4)  x1 ~x3 ~x3 ~x2 x2 ~x4
  • 4.
    観察 ● ここで、「変数の出現回数は 1 or 2 回」 に注目してみよう。 ● この条件から、各ノードの最大次数は 2 以下。 ● グラフは 線 or  環 が集まったものになる。 ● 連結成分毎に考えて最後に掛け合わせれば良い。 次数 3
  • 5.
    特別な場合 ● ひとつのブロックに同じ変数が入っていた場合 : ● (x ∨ x) や (~x∨~x) のような場合は 1 を返す ● (x ∨ ~x) のような場合は 2 を返す
  • 6.
    線の場合 ● dp[i][j] := {i 番目のブロックで、 i+1 番目のブロック と隣接するノードの値は j であり、 1~i 番のブロッ クは全て true であるような割り当ての数 } ● 下の図では、赤い辺は 0, 1 が反転するようなペア で、黄の辺は 0, 1 が等しいようなペア 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 ~x6
  • 7.
    線の場合 ● dp[1][1] = 2, dp[1][0] = 1 ● 最終的な答は   dp[N][0] + dp[N][1] となる。 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 ~x6
  • 8.
    線の場合 ● ブロック i と i­1 が赤い辺でつながっているとき: ● dp[i][0] = dp[i­1][0] ● dp[i][1] = dp[i­1][1] + dp[i­1][0] 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 ~x6
  • 9.
    線の場合 ● ブロック i と i­1 が黄の辺でつながっているとき: ● dp[i][0] = dp[i­1][1] ● dp[i][1] = dp[i­1][1] + dp[i­1][0] 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 ~x6
  • 10.
    環の場合 ● 環になっている場合は、どこかで切断して無理やり 線の場合に落とす。 ● 切ったときに、 1 番のブロックに入っているペアの 片割れが 0 になるか 1 になるか両方試す。 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 x1
  • 11.
    環の場合 ● 切断した箇所が赤い辺の場合は以下の 2 つの和: ● 左端を 1 にした場合 : dp[1][1] = 1, dp[1][0] = 1 と して dp[N][0] を求める。 ● 左端を 0 にした場合: dp[1][1] = 1, dp[1][0] = 0 と して dp[N][1] を求める。 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 ~x1
  • 12.
    環の場合 ● 切断した箇所が黄の辺の場合は以下の 2 つの和: ● 左端を 1 にした場合: dp[1][0] = 1, dp[1][1] = 1 と して dp[N][1] を求める。 ● 左端を 0 にした場合: dp[1][0] = 0, dp[1][1] = 1 と して dp[N][0] を求める。 1 2 3 4 5 x1 ~x3 ~x3 x5 ~x5 x2 ~x2 x4 x4 x1
  • 13.
    計算量 ● グラフのビルド、 DP の計算ともに O(N) 。
  • 14.
    解答例 ● 宮村 (C++)   127 行 2577 byte ● 橋本 (Java)   161 行 4841 byte