最大クリークの計算量とか(続き)
kousuke.hatenablog.com
大事なことに気が付いたので、前回の修正。
単純な無向グラフに対し、をサイズkのクリークが含まれるかどうかを判定する関数とする。
グラフGに自身を除く全頂点に接続する頂点がある場合、それらは必ず最大クリークに含まれる。そのような頂点集合をとすると、下記が成り立つ。
の場合を考える。
頂点vに隣接している頂点の集合をとし、に対し、とする。ならば、eを含むサイズ3のクリークが存在し、誘導部分グラフの中にeを含むクリークが全て含まれる。よって次式が成り立つ。
の頂点はすべてeに接続しているので、にサイズk-2のクリークがあればよいので、さらに次式のように変形できる。
終了条件も含め、下記のようになる。
またにサイズkのクリークが含まれない場合、Gからeを取り除いても最大クリークには影響しない。
は辺を評価していると見ることができる。とする。
であれば、即座にその辺を除去する前処理を行う。この前処理の結果、少なくともサイズ3のクリークをk-2個以上含む辺のみが残り、各頂点の次数はk-1以上になる。
また次数がk-1未満になる頂点はその時点で除去できる。そのため、前処理後のグラフに対し、すべての辺を評価しなくともよい。
評価する辺の集合が最小のものをとし、を取り除いたグラフをとする。
下式よりおよその辺を評価すれば良いことがわかる。
最悪でも深さk/2の有界探索木だとおもうのでの目がある。
結局FPTなんだけど、そのパラメーターは極大クリークの数に依存する。上記のワーストケースは常にの場合で、どんなにクリークサイズが大きくても、完全グラフ-数辺みたいのだとあっという間に終わる。
前回は全点に接続する頂点を書いてなくて、終了条件に完全グラフって書いてたけど、これでぐっと見通しがよくなった。
次は補グラフでの最大独立集合を考えることにする。
を使った前処理の実装例
#!/usr/bin/env ruby require 'optparse' def shave_by_clique(g, eset, c) eset.reject {|u,v| (g[v] & g[u]).size < c } end def build_graph(g,vset,eset) vset.each {|v| g[v] = [] } eset.each {|u,v| g[v] << u; g[u] << v } end c = 3 opt = OptionParser.new opt.on('-c C', 'lower number bounds of 3-cliques.'){|v| c = v.to_i } opt.parse!(ARGV) n, m = gets.split.map(&:to_i) V = n.times.to_a E = m.times.map { gets.split.map(&:to_i) } G = {} eset = E build_graph(G,V,E) loop do shaved = shave_by_clique(G, eset, c - 2) if eset.size == shaved.size break end G.clear build_graph(G, V, shaved) eset = shaved end vset = eset.flatten.uniq.sort index = {} vset.each_with_index{|v,i| index[v] = i } puts "#{vset.size} #{eset.size}" puts eset.map{|e| e.map{|v| index[v] }}.map{|e| e.join(' ')} puts "# #{vset.join(' ')}"