START_TIME = Time.utc.to_unix_ms TL = 1580 N = 2048 M = 256 INF = 1 << 29 RND = Random.new(2) macro debug(msg) {% if flag?(:trace) %} STDERR.puts({{msg}}) {% end %} end macro debugf(format_string, *args) {% if flag?(:trace) %} STDERR.printf({{format_string}}, {{*args}}) {% end %} end class IndexSet getter :upper def initialize(@upper : Int32) @que = [] of Int32 @pos = Array(Int32).new(@upper, -1) end def add(v) if @pos[v] == -1 @pos[v] = @que.size @que << v end end def remove(v) p = @pos[v] b = @que[-1] @que[p] = b @que.pop @pos[b] = p @pos[v] = -1 end def get_random @que[RND.rand(@que.size)] end def size @que.size end def to_s(io) @que.to_s(io) end end class Result property :bits, :score def initialize(@bits : Array(Int32), @score : Int32) end def to_s(io) io << @bits.map { |v| v == -1 ? 0 : v }.reverse.join end end struct Cond getter :var, :val def initialize(@var : Int32, @val : Int32) end end alias Term = Tuple(Cond, Cond, Cond) class Solver def initialize @terms = Array(Term).new(N) do a, b, c, p, q, r = read_line.split.map(&.to_i) {Cond.new(a, p), Cond.new(b, q), Cond.new(c, r)} end @var_pos = Array(Array(Tuple(Int32, Int32))).new(M) { [] of Tuple(Int32, Int32) } N.times do |i| 3.times do |j| term = @terms[i][j] @var_pos[term.var] << {i, term.val} end end @best_result = Result.new(Array.new(M, -1), 0) @ans = Array(Int32).new(M, -1) @search_order = [0, 1, 2] @dfs_count = 0 @eval = Array(Int32).new(3, 0) @ok_count = Array(Int32).new(N, 0) @ng_count = Array(Int32).new(N, 0) @pass = Array(Bool).new(N) do |i| ts = @terms[i] [[0, 1], [1, 2], [2, 0]].any? { |ps| ts[ps[0]].var == ts[ps[1]].var && ts[ps[0]].var != ts[ps[1]].var } end end def solve(timelimit) start_time = Time.utc.to_unix_ms before_time = start_time solve_single() after_time = Time.utc.to_unix_ms worst_time = after_time - before_time turn = 0 while true before_time = after_time if before_time + worst_time > timelimit debug("turn:#{turn} worst_time:#{worst_time}") break end solve_single() after_time = Time.utc.to_unix_ms worst_time = {worst_time, after_time - before_time}.max turn += 1 end STDERR.puts("validate_score:#{score(@best_result.bits)}") return @best_result end def solve_single @ans.fill(-1) @dfs_count = 0 @ok_count.fill(0) @ng_count.fill(0) pos = 0 while pos < N if try_next(pos) while pos < N && @ok_count[pos] > 0 pos += 1 end if pos > @best_result.score @best_result.score = pos @best_result.bits[0, M] = @ans debug("best_score:#{pos}") end # if score(@ans) != pos # debug("validate_score:#{score(@ans)} score:#{pos}") # exit # end else break end end end def try_next(pos) shuffle(@search_order) 3.times do |i| t = @terms[pos][@search_order[i]] if @ans[t.var] == -1 @ans[t.var] = t.val @var_pos[t.var].each do |ps| p, v = ps if v == t.val @ok_count[p] += 1 else @ng_count[p] += 1 end end return true end end set = IndexSet.new(N) set.add(pos) used = Set(Int32).new @dfs_count = 0 return try_dfs(pos, set, used) end def try_dfs(pos, set, used) # debug("try_dfs:#{pos} #{set} #{used}") if set.size == 0 return true end if @dfs_count > 10000 return false end cur = set.get_random # set.remove(cur) # if @ok_count[cur] > 0 # if try_dfs(pos, set, used) # return true # else # set.add(cur) # return false # end # end shuffle(@search_order) @dfs_count += 1 3.times do |i| t = @terms[cur][@search_order[i]] next if used.includes?(t.var) used << t.var old = @ans[t.var] @ans[t.var] = t.val @var_pos[t.var].each do |ps| p, v = ps if v == t.val @ok_count[p] += 1 @ng_count[p] -= 1 if p <= pos && @ok_count[p] == 1 set.remove(p) end elsif old != -1 @ok_count[p] -= 1 @ng_count[p] += 1 if p <= pos && @ok_count[p] == 0 set.add(p) end end # debug("@ok_count[#{p}]=#{@ok_count[p]}") # debug("@ng_count[#{p}]=#{@ng_count[p]}") end if try_dfs(pos, set, used) return true end used.delete(t.var) @ans[t.var] = old @var_pos[t.var].each do |ps| p, v = ps if v == t.val @ok_count[p] -= 1 @ng_count[p] += 1 if p <= pos && @ok_count[p] == 0 set.add(p) end elsif old != -1 @ok_count[p] += 1 @ng_count[p] -= 1 if p <= pos && @ok_count[p] == 1 set.remove(p) end end end end # set.add(cur) return false end def solve_single2 @ans.fill(-1) @dfs_count = 0 @ok_count.fill(0) @ng_count.fill(0) dfs(0) end def dfs(pos) if @pass[pos] dfs(pos + 1) end if pos > @best_result.score @best_result.score = pos @best_result.bits[0, M] = @ans debug("best_score:#{pos}") end if @dfs_count > 1000 return end if @terms[pos].any? { |t| @ans[t.var] == t.val } dfs(pos + 1) else 3.times do |i| t = @terms[pos][i] if @ans[t.var] != -1 @eval[i] = -INF next end @eval[i] = RND.rand(5000000) @var_pos[t.var].each do |ps| p, v = ps next if p <= pos if v == t.val if @ok_count[p] == 0 # @eval[i] += N - p end else if @ok_count[p] != 0 # @eval[i] -= (N - p) * ((@ng_count[p] + 1) ** 2) if p <= @best_result.score && @ng_count[p] == 2 @eval[i] -= INF * 2 end end end end end # debug(@eval) cands = 3.times.select { |i| @eval[i] > -INF }.to_a return if cands.empty? @dfs_count += 1 if cands.size > 1 cands.sort_by { |i| -@eval[i] }.each do |ti| t = @terms[pos][ti] @ans[t.var] = t.val @var_pos[t.var].each do |ps| p, v = ps if p > pos if v == t.val @ok_count[p] += 1 else @ng_count[p] += 1 end end end dfs(pos + 1) @var_pos[t.var].each do |ps| p, v = ps if p > pos if v == t.val @ok_count[p] -= 1 else @ng_count[p] -= 1 end end end @ans[t.var] = -1 end end end def shuffle(a) (a.size - 1).times do |i| pos = RND.rand(a.size - i) + i a[i], a[pos] = a[pos], a[i] end end def score(ans) N.times do |i| if @terms[i].all? { |t| ans[t.var] != t.val } return i end end return N end end def main solver = Solver.new res = solver.solve(START_TIME + TL) puts res {% if flag?(:local) %} STDERR.puts("score:#{res.score}") {% end %} end main