AOJ(問題集)7
AIZU ONLINE JUDGE: Programming Challenge
0061 Rank Checker
data = [] until (st = $<.gets.chomp) == "0,0" data << st.split(",").map(&:to_i) end data.sort! {|a, b| b[1] <=> a[1]} x = data.first.last h = {} l = 1 data.each do |d| l += 1 unless x == d.last x = d.last h[d.first] = l end puts $<.readlines.map(&:to_i).map {|i| h[i]}
実装が汚い…。
0062 What is the Bottommost?
def doit(ary) return ary.first if ary.size == 1 doit( ary.each_cons(2).map {|i, j| (i + j) % 10} ) end $<.readlines.map {|a| a.chomp.chars.map(&:to_i)}.each do |given| puts doit(given) end
0063 Palindrome
co = 0 $<.readlines.map(&:chomp).each do |st| co += 1 if st == st.reverse end puts co
0064 Secret Number
n = 0 $<.readlines.map(&:chomp).each do |st| n += st.scan(/\d+/).map(&:to_i).sum end puts n
0065 Trading
x, y = $<.readlines.map(&:chomp).chunk {|st| st.empty?}.reject {|a| a.first} .map(&:last).map{|a| a.map {|b| b.split(",").first.to_i}}.to_a h = Hash.new(0) (x + y).each {|i| h[i] += 1} (x - (x - y)).uniq.sort.each {|i| puts "#{i} #{h[i]}"}
0066 Tic Tac Toe
table = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]] check = ->(field, type) { table.each {|pat| return true if pat.map {|i| field[i] == type}.all?} false } $<.readlines.map(&:chomp).map(&:chars).each do |field| result = if check.(field, "o") "o" elsif check.(field, "x") "x" else "d" end puts result end
最初与えられた盤面でゲームが完結していないのかと思った…。
0067 The Number of Island
L = 12 def delete(field, x, y) field[y][x] = "0" [[1, 0], [0, -1], [-1, 0], [0, 1]].each do |dx, dy| x1 = x + dx y1 = y + dy if !(x1 < 0 or x1 >= L or y1 < 0 or y1 >= L) and field[y1][x1] == "1" delete(field, x1, y1) end end end $<.readlines.map(&:chomp).chunk {|st| st.empty?} .reject {|a| a.first}.map {|a| a.last}.each do |field| co = 0 L.times do |y| L.times do |x| if field[y][x] == "1" co += 1 delete(field, x, y) end end end puts co end
0068 Enclose Pins with a Rubber Band
include Math until (n = $<.gets.to_i).zero? points = Array.new(n) { Complex(*$<.gets.split(",").map(&:to_f)) } ym = points.map(&:imaginary).max start_po = po0 = points.find {|po| po.imaginary == ym} prev_arg = PI result = loop do points = (points - [start_po] + [po0]).uniq selected = points.map {|po| [po - start_po, po]} .reject {|pos| pos.first == 0} .sort_by {|pos| a = pos.first.angle + PI - prev_arg; a >= 0 ? a : a + 2 * PI} .first.last points.select! {|po| po != start_po} break points.size - 1 if selected == po0 # po0の分だけ1引く prev_arg = (selected - start_po).angle + PI start_po = selected end puts result end
ムズカしかったので一発でいってうれしいが、しかし複雑すぎる。もっといい解法がないかな。
Ruby で解けている人は多くない。
問題はこれ。考え方としては
1. 点を複素数としてオブジェクト化する。
2. 確実に外にある点(po0
)をひとつ選ぶ。
3. 残りの点のすべてへの方向を計算し、いちばん少ない角度で左回りになる点を選ぶ。
4. 選んだ点で同様に (3) を繰り返し、一周したところでおしまい。
5. 残った点の数が求めるもの。
つまり、反時計回りで一周して、周にない点の数を求めるということ。
0069 Drawing Lots II
あみだくじ。与えられたあみだくじで特定の場所を選んで当たりに到達するか調べる。もしそれでダメなら、一本だけ横線を加えてもよい。という問題(参照)。
def hit(n, m, goal, d, dans) h = 0 until h == d yoko = dans[h] if m != n - 1 and yoko[m] == 1 m += 1 elsif m.nonzero? and yoko[m - 1] == 1 m -= 1 end h += 1 end goal == m end until (n = $<.gets.to_i).zero? m = $<.gets.to_i - 1 goal = $<.gets.to_i - 1 d = $<.gets.to_i dans = Array.new(d) { $<.gets.chomp.chars.map(&:to_i) } if hit(n, m, goal, d, dans) puts 0 else try = ->{ d.times do |h| ([0] + dans[h] + [0]).each_cons(3).with_index do |a, x| if a.sum.zero? dans[h][x] = 1 return "#{h + 1} #{x + 1}" if hit(n, m, goal, d, dans) dans[h][x] = 0 end end end 1 } puts try.() end end
これも一発でいった。しかし、段々ムズカしくなってきた…。ふう。
これも Ruby で解いた人は少ない。
0070 Tag Discussion Solution Statistics Submit
def solve(ary, n, s) co = 0 ary.each do |i| s1 = s - i * n if n == 1 if s1.zero? co += 1 end elsif s1 > 0 co += solve(ary - [i], n - 1, s1) end end co end $<.readlines.map {|a| a.split.map(&:to_i)}.each do |n, s| puts solve([*0..9], n, s) end
たぶんこれでいけるのだけれど、時間制限に引っかかる。問題はこれ。よく考えないとな。
デバッグ用。
io.readlines.map {|a| a.split.map(&:to_i)}.each do |n, s| solve = ->(ary, n, s1, st = "") { co = 0 ary.each do |i| s2 = s1 - i * n st1 = st + "#{i}*#{n} + " if n == 1 if s2.zero? co += 1 puts st1[0..-4] + " = #{s}" end elsif s2 > 0 co += solve.(ary - [i], n - 1, s2, st1) end end co } puts solve.([*0..9], n, s) end
これはたとえば n = 5, s = 24 でこうなる。
0*5 + 1*4 + 2*3 + 3*2 + 8*1 = 24 0*5 + 1*4 + 2*3 + 4*2 + 6*1 = 24 0*5 + 1*4 + 2*3 + 5*2 + 4*1 = 24 0*5 + 1*4 + 3*3 + 2*2 + 7*1 = 24 0*5 + 1*4 + 4*3 + 3*2 + 2*1 = 24 0*5 + 2*4 + 1*3 + 3*2 + 7*1 = 24 0*5 + 2*4 + 1*3 + 4*2 + 5*1 = 24 0*5 + 2*4 + 1*3 + 5*2 + 3*1 = 24 0*5 + 2*4 + 3*3 + 1*2 + 5*1 = 24 0*5 + 3*4 + 1*3 + 2*2 + 5*1 = 24 0*5 + 3*4 + 2*3 + 1*2 + 4*1 = 24 1*5 + 0*4 + 2*3 + 3*2 + 7*1 = 24 1*5 + 0*4 + 2*3 + 4*2 + 5*1 = 24 1*5 + 0*4 + 2*3 + 5*2 + 3*1 = 24 1*5 + 0*4 + 3*3 + 2*2 + 6*1 = 24 1*5 + 0*4 + 3*3 + 4*2 + 2*1 = 24 1*5 + 0*4 + 4*3 + 2*2 + 3*1 = 24 1*5 + 2*4 + 0*3 + 3*2 + 5*1 = 24 1*5 + 2*4 + 0*3 + 4*2 + 3*1 = 24 2*5 + 0*4 + 1*3 + 3*2 + 5*1 = 24 2*5 + 0*4 + 1*3 + 4*2 + 3*1 = 24 2*5 + 1*4 + 0*3 + 3*2 + 4*1 = 24 22
フーム。
関係性を調べてみる。
table = [] s = 0 while s < 25 st = "" table << (1..10).map {|n| "%3d" % solve([*0..9], n, s)}.join(" ") s += 1 end table.reverse_each {|l| puts l}
結果。
0 1 16 43 22 0 0 0 0 0 0 3 27 51 13 0 0 0 0 0 0 3 21 35 7 0 0 0 0 0 0 3 18 36 5 0 0 0 0 0 0 4 20 26 1 0 0 0 0 0 0 5 23 29 0 0 0 0 0 0 0 3 13 19 0 0 0 0 0 0 0 5 20 23 0 0 0 0 0 0 0 4 14 15 0 0 0 0 0 0 0 4 13 14 0 0 0 0 0 0 0 4 12 11 0 0 0 0 0 0 0 5 14 9 0 0 0 0 0 0 0 3 7 4 0 0 0 0 0 0 0 5 11 4 0 0 0 0 0 0 0 4 8 1 0 0 0 0 0 0 1 4 5 0 0 0 0 0 0 0 1 4 4 0 0 0 0 0 0 0 1 4 5 0 0 0 0 0 0 0 1 2 2 0 0 0 0 0 0 0 1 3 3 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
うーん。
かなり考えたのだが、これでも時間オーバー。メモ化。
@memo = {} def solve(ary, n, s) hash = ary.map {|i| [11, 13, 17, 19, 23, 29, 31, 37, 41, 43][i]}.inject(&:*) hash *= n return @memo[[hash, s]] if @memo[[hash, s]] co = 0 ary.each do |i| s1 = s - i * n if n == 1 if s1.zero? co += 1 end elsif s1 > 0 co += solve(ary - [i], n - 1, s1) end end @memo[[hash, s]] = co end $<.readlines.map {|a| a.split.map(&:to_i)}.each do |n, s| puts solve([*0..9], n, s) end
このアプローチでは無理かな。
他の人の回答(参照)。
$sumhash = {} def check_sum(n, s, used = 0) key = "#{n},#{s},#{used}" return $sumhash[key] if ! $sumhash[key].nil? if n == 0 return s == 0 ? 1 : 0 end count = 0 10.times do |i| b = 1 << i ni = n * i if (used & b).zero? and s >= ni used |= b count += check_sum(n - 1, s - ni, used) used ^= b end end return $sumhash[key] = count end while (line = $<.gets) n, s = line.chomp.split(" ").map{|s| s.to_i} puts check_sum(n, s) end
なんと、じつに素直なアプローチ。で、あとはメモ化かあ。全然自分の知っている方法だけで解けるではないか。うーん、まだまだだなあ…。
ただ、ビット演算で or で立てたフラグを消すのが xor というのは気づかなかった。これは勉強になった。