続: 最大クリーク問題
なんかいろいろやってこんな形に落ち着いた。
簡単にいうとまず適当にソートして、有向グラフに変換してDPする。
dp[j][c]はj以下の頂点を使用したjを含むサイズcのクリークの文字列表現の配列。
後続の頂点iからはこのクリークの頂点にすべて辺があれば、サイズc+1のクリークなのでdp[i][c+1]に追加する。
このクリークの頂点にすべて辺があればの部分は、rubyなので配列を&した、他の言語だとbitsetみたいなライブラリで行うほうがよいと思う。
belongs_max_cliqueは前回のソースを引き継いでいるので現在は適当ではないかもしれないけど、ある頂点vに隣接する誘導グラフから、最大クリークサイズをだす場合はbelongs_max_clique(G[v]) + 1とすればよいようになっている。
#!/usr/bin/env ruby V, m = gets.split.map(&:to_i) # read Graph Infomation G = {} # Initialize Graph V.times {|v| G[v] = []} m.times do from, to = gets.split.map(&:to_i).sort G[from] << to G[to] << from end def belongs_max_clique(node = V.times.to_a, limit = V) subgraph = {} node.sort! node.each_index{|i| subgraph[i] = [] } node.each_with_index do |u, i| i.times do |j| w = node[j] subgraph[i] << j if G[u].include?(w) end end dp = Array.new(node.size + 1){ Array.new(limit + 1){ [] } } node.each_index{|i| dp[i][1] << i.to_s } max_clique_size = (1 ... limit).find do |c| found_clique = false node.each_index do |i| subgraph[i].each do |j| dp[j][c].each do |clq| clique = clq.split.map(&:to_i) shared = clique & subgraph[i] if shared == clique clique << i dp[i][c + 1] << clique.join(' ') dp[node.size][c + 1] << clique.join(' ') found_clique = true end end end end !found_clique end max_clique_size || limit end puts belongs_max_clique
前回指摘をもらったグラフだとDPした結果は以下のようになる
[[], ["0"], [], [], [], [], [], [], []], [[], ["1"], ["0 1"], [], [], [], [], [], []], [[], ["2"], ["0 2", "1 2"], ["0 1 2"], [], [], [], [], []], [[], ["3"], ["0 3", "1 3", "2 3"], ["0 1 3", "0 2 3", "1 2 3"], ["0 1 2 3"], [], [], [], []], [[], ["4"], ["0 4"], [], [], [], [], [], []], [[], ["5"], ["0 5", "4 5"], ["0 4 5"], [], [], [], [], []], [[], ["6"], ["0 6", "5 6"], ["0 5 6"], [], [], [], [], []], [[], ["7"], ["0 7", "4 7", "6 7"], ["0 4 7", "0 6 7"], [], [], [], [], []],
入力はこんな感じ
8 14 0 1 0 2 0 3 1 2 1 3 2 3 0 4 0 5 0 6 0 7 4 7 4 5 5 6 6 7
dp[node.size][c]は別になくてもよいけど、サイズcのクリーク( c >= 2)の全列挙用につくっている。
こんな感じになる。
[], ["0 1", "0 2", "1 2", "0 3", "1 3", "2 3", "0 4", "0 5", "4 5", "0 6", "5 6", "0 7", "4 7", "6 7"], ["0 1 2", "0 1 3", "0 2 3", "1 2 3", "0 4 5", "0 5 6", "0 4 7", "0 6 7"], ["0 1 2 3"]
追記: コメントに計算量のことを書いて質問をいただきました。現在のコードがc^2 * v * eかと言われれば、ご指摘の通りたぶん違うんじゃないかとおもってきました。ただ自分的にはc^2 * v * eにまでおそらく落とせると思っているのでそこまで計算量を落としたアルゴリズムを作りたいと思っています。
メモ: cのループをnodeのループの中に入れて、サイズcのクリークの全列挙をやめると計算量は落ちるな。でもやはりサイズcのクリーク数に依存しそうだから、サイズcのクリークの個数に対して、cかc^2になりそうかな。計算量は落ち着かないけど、これならできそう。