NUM_GUESSES = 20 NUM_GOOD = 10 class Column def initialize(map, x) @map = map; @x = x end def [](y) @map[y] and @map[y][@x] end def length() @map.length end end $dbg_counter = Hash.new(0) def _dbg(msg, val) #$stderr.puts "+++ " + msg $dbg_counter[msg] += 1 val end def progress_start(message, total) $prg_message = ("\b"*(message.length+8)) + message + "... " $prg_total = total; $prg_current = 0; $prg_last = 0 $stderr.print $prg_message end def progress() return if not $prg_message $prg_current += 1 if 100*$prg_current/$prg_total != $prg_last $prg_last = 100*$prg_current/$prg_total $stderr.print $prg_message + $prg_last.to_s + "%" end end def progress_end() return if not $prg_message $stderr.print $prg_message + "done!\n" $prg_message = nil end def choice_is_ok(sure_values, info, positions) o = 0; size = sure_values.length positions.each_with_index do |p, i| p.times { return false if sure_values[o] and sure_values[o] != 0; o += 1 } info[i].times { return false if sure_values[o] and sure_values[o] != 1; o += 1 } 1.times { return false if sure_values[o] and sure_values[o] != 0; o += 1 } end (size-o).times { return false if sure_values[o] and sure_values[o] != 0; o += 1 } return true end def compute_whites(info, size) info.each { |b| size -= b } size + 1 - info.length end def svio_guess_ok_choice(sure_values, info) whites = compute_whites(info, sure_values.length) NUM_GUESSES.times do |gn| guess = Array.new(info.length).map{ rand(whites/info.length + 3) } guess_sum = 0; guess.each { |w| guess_sum += w } return _dbg("guess #{gn}", true) if guess_sum <= whites and choice_is_ok(sure_values, info, guess) end _dbg("didn't guess it.", nil) false end def svio_false_chain_exists(sure_values, info) sure_values.length.times do |i| if sure_values[i] == 1 and (i == 0 or sure_values[i-1] != 1) first = i; last = i; last += 1 while sure_values[last+1] == 1 and last+1 < sure_values.length char_before = (first == 0 ? 0 : sure_values[first-1]) char_after = (last == sure_values.length-1 ? 0 : sure_values[last+1]) chain_length = last-first+1 return _dbg("too long chain", true) if not info.detect{ |b| b >= chain_length } if char_before == 0 and char_after == 0 return _dbg("no exact chain match", true) if not info.detect{ |b| b == chain_length } end end end _dbg("didn't find false", nil) false end def svio_find_ok_choice(sure_values, info) whites = compute_whites(info, sure_values.length) pos_sum = 0; positions = Array.new(info.length, 0) while true return true if choice_is_ok(sure_values, info, positions) return _dbg("false!", false) if positions[0] == whites if pos_sum == whites i = -1; i -= 1 while positions[i] == 0 pos_sum -= positions[i]; positions[i] = 0 pos_sum += 1; positions[i-1] += 1 else pos_sum += 1; positions[-1] += 1 end end end def sure_values_is_ok(row, col, row_info, col_info) # (checks if there is a choice that's valid against row and col) # first check if it definitely is false return false if svio_false_chain_exists(row, row_info) or svio_false_chain_exists(col, col_info) # try to guess some true choices. if guessing doesn't work, loop through all choices row_ok = (svio_guess_ok_choice(row, row_info) or svio_find_ok_choice(row, row_info)) col_ok = (svio_guess_ok_choice(col, col_info) or svio_find_ok_choice(col, col_info)) return (row_ok and col_ok) end def solve_square(map, x, y, row, col, row_info, col_info) return if map[y][x] map[y][x] = 1 # let's assume it'll be 1 if not sure_values_is_ok(row, col, row_info, col_info) map[y][x] = 0; progress; dump_map(map); return end map[y][x] = 0 # let's assume it'll be 0 if not sure_values_is_ok(row, col, row_info, col_info) map[y][x] = 1; progress; dump_map(map); return end map[y][x] = nil end def solve(row_info, col_info) w = col_info.length; h = row_info.length map = Array.new(h).map{ Array.new(w) } cols = (0...w).collect{ |x| Column.new(map, x) } progress_start("solving", w*h) # first, find some promising rows/cols good_cols = (0...w).select { |i| compute_whites(col_info[i], h) < col_info[i].max } good_rows = (0...h).select { |i| compute_whites(row_info[i], w) < row_info[i].max } p good_cols p good_rows good_cols.each { |x| h.times { |y| solve_square(map, x, y, map[y], cols[x], row_info[y], col_info[x]) } } good_rows.each { |y| w.times { |x| solve_square(map, x, y, map[y], cols[x], row_info[y], col_info[x]) } } while true (0...w*h).collect{|i| [i % w, i / w] }.each do |c| x,y = c solve_square(map, x, y, map[y], cols[x], row_info[y], col_info[x]) end break if dump_map(map) end progress_end end def dump_map(map) solved = true sym = [ ".", "X" ] mapdump = map.collect{ |row| row.collect{ |f| solved = false if not f; f ? sym[f] : " " }.join + "\n" }.join $stderr.puts "-----\n"+mapdump #if solved return solved end def read_array(file) data = [] while true line = file.readline return data if line.chomp == "---" data << line.gsub(/\D+/, " ").strip.split(" ").map{ |s| s.to_i } end end rows = read_array($stdin) cols = read_array($stdin) solve(rows, cols) p $dbg_counter