読者です 読者をやめる 読者になる 読者になる

最大クリーク問題 試行

いままでと全然変えてみてある辺(i,j)を含む、順序上 j以下の頂点で構築できる最大クリークとか考えてみた。
サイズcのクリークがある時、全点から接続している点はc+1のクリークって考えると凄くコンパクト。

でも2^cとか含むだろうけどね。

n,m = gets.split.map(&:to_i)
G = {}
U = {}
V = n.times.to_a
E = m.times.map do
    gets.split.map(&:to_i).sort
end
E.sort!

V.each do |v| 
   G[v] = []
   U[v] = [] 
end
E.each do |from, to| 
    G[from] << to
    G[to] << from
    U[to] << from
end

MEMO = {}

def dfs(clique, c)
    clique.sort!
    s,t = clique.first, clique.last
    status = clique.join(' ')
    return MEMO[status] if MEMO[status]
    
    nclique = clique.inject(U[t]){|n, v| n & G[v] }.sort
    nc = nclique.empty? ? c : nclique.map {|nv| dfs(clique + [nv], c+1) }.max
    p clique: clique, c: nc
    MEMO[status] = nc
end

E.each do |e|
    puts "#{e} #{dfs(e, 2)}"
end


なんとなくこのコードが引っかかる。

再帰呼び出しc回でサイズcの極大クリークが見つかる、サイズcのクリークは2^c - 1個のクリークがあるけど、なんとなくうまく枝刈りするとサイズがcの極大クリーク(他のクリークに含まれないの意味)をc^2のオーダーで抜けれそうな気がするんだよね。

例えばこんな感じのコードかなぁ。

n,m = gets.split.map(&:to_i)
VISITED = {}

G = {}
V = n.times.to_a
E = m.times.map { gets.split.map(&:to_i).sort }

V.each {|v| G[v] = [] }
E.each do |from, to|
    G[from] << to
    G[to] << from
end

def dfs(clique, max)
    clique.sort!
    status = clique.join(' ')
    
    return max if VISITED[status]
    
    VISITED[status] = true
    
    if clique.size > max
        max = clique.size
        # p clique: clique, max: max
    end
    
    head, *body = clique
    nclique = body.inject(G[head]){|n, v| n & G[v] }
    if clique.size + nclique.size > max
        max = nclique.inject(max) {|m, u| dfs(clique + [u], m) } 
    end
    
    max
end

if n == 0
    puts 0
elsif m == 0
    puts 1
else
    puts E.inject(0){|max, e| dfs(e, max) }
end

下記入力に対して

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


こういう動き

{:clique=>[0, 1], :max=>2}
{:clique=>[0, 1, 2], :max=>3}
{:clique=>[0, 1, 2, 3], :max=>4}
{:clique=>[0, 1, 3], :max=>4}
{:clique=>[0, 2], :max=>4}
{:clique=>[0, 3], :max=>4}
{:clique=>[1, 2], :max=>4}
{:clique=>[1, 3], :max=>4}
{:clique=>[2, 3], :max=>4}
{:clique=>[0, 4], :max=>4}
{:clique=>[0, 5], :max=>4}
{:clique=>[0, 6], :max=>4}
{:clique=>[0, 7], :max=>4}
{:clique=>[4, 7], :max=>4}
{:clique=>[4, 5], :max=>4}
{:clique=>[5, 6], :max=>4}
{:clique=>[6, 7], :max=>4}

サイズcのクリークの全点から接続している点は必ずサイズc+1のクリークで、これまでに見つかった最大クリークのサイズをmaxとすると、サイズc+1のクリークがmax - c個より多く存在しないとmax + 1のサイズのクリークはないので枝刈ってみた。

Submission #1100279 - AtCoder Beginner Contest 002 | AtCoderの結果

これcarraghanのアルゴリズムっぽい。。。
https://www.jstage.jst.go.jp/article/ciqs/2006/0/2006_0_JP04/_pdf