AOJ(問題集)21

0200 Traveling Alone: One-way Ticket of Youth

until (given = gets.split.map(&:to_i)) == [0, 0]
  n, m = given
  edge = Array.new(m) {[]}
  h = Array.new(m) {[]}
  n.times do
    a, b, cost, time = gets.split.map(&:to_i)
    a -= 1
    b -= 1
    edge[a][b] = edge[b][a] = [cost, time]
    h[a] << b
    h[b] << a
  end
  
  gets.to_i.times do
    p, q, r = gets.split.map(&:to_i)
    p -= 1
    q -= 1
  
    shortest = Array.new(m, Float::INFINITY)
    done = m.times.map(&:itself)
    shortest[p] = 0
    
    until done.empty?
      u = done.min {|a, b| shortest[a] <=> shortest[b]}
      done.delete(u)
      
      h[u].each do |v|
        if (a = shortest[u] + edge[u][v][r]) < shortest[v]
          shortest[v] = a
        end
      end
    end
    
    puts shortest[q]
  end
end

ダイクストラ法なのだが、他の人の回答を見て徹底的に高速化する。これで 3.30秒。
 

0201 Wrought Gold Master

until (n = gets.to_i).zero?
  costs = n.times.map { gets.split.tap {|a| a[1] = a[1].to_i} }.to_h
  
  m = gets.to_i
  recipe = Hash.new {|hash, key| hash[key] = []}
  m.times do
    given = gets.split
    recipe[given[0]] += given[2..-1]
  end
  goal = gets.chomp
  
  try = ->(parent) {
    r = recipe[parent]
    c = costs[parent]
    if r.empty?
      c
    else
      made = r.map {|child| try.(child)}.sum
      [made, c].min
    end
  }
  puts try.(goal)
end

DFS(深さ優先探索)。錬金釜で作った方が安いかチェックを入れる。
 

0202 At Boss's Expense

require "prime"

until (given = io.gets.split.map(&:to_i)) == [0, 0]
  dishes = given[0].times.map {io.gets.to_i}
  
  result = 0
  dp = Array.new(given[1] + 1, false)
  dp[0] = true
  
  (1..given[1]).each do |i|
    dishes.each do |price|
      if i >= price && dp[i - price]
        dp[i] = true
        break
      end
    end
    result = i if dp[i] && i.prime?
  end
  
  puts result.zero? ? "NA" : result
end

時間オーバー。これは Ruby ではかなり無理っぽい。出来ている人は一人しかいない。
 

0203 A New Plan of Aizu Ski Resort

loop do
  w, h = gets.split.map(&:to_i)
  break if w + h == 0
  
  field = h.times.map {gets.split.map(&:to_i)}
  dp = Array.new(h) {Array.new(w, 0)}
  w.times {|x| dp[h - 1][x] = 1 if field[h - 1][x] != 1}
  
  (h - 2).downto(0) do |y|
    w.times do |x|
      case field[y][x]
      when 2
        if y + 2 == h
          dp[y][x] = 1
        else
          dp[y][x] = dp[y + 2][x]
        end
      when 0
        s = 0
        [-1, 0, 1].each do |dx|
          x1 = x + dx
          next if x1 < 0 || x1 == w
          next if dx.nonzero? && field[y + 1][x1] == 2
          s += dp[y + 1][x1]
        end
        dp[y][x] = s
      end
    end
  end
  
  puts dp[0].sum
end

動的計画法これをパクリましたよ。いやー、むずかしいなあ。
 

0204 UFO Shooting Down Operation

class Ufo
  def initialize(x, y, r, v)
    @dist = Math.sqrt(x ** 2 + y ** 2)
    @x = x
    @y = y
    @r = r
    @v = v
    @ex = x / @dist
    @ey = y / @dist
    @vx = -v * @ex
    @vy = -v * @ey
  end
  attr_reader :x, :y, :r, :dist, :ex, :ey
  
  def move
    @dist -= @v
    @x += @vx
    @y += @vy
  end
end

loop do
  radius, n = gets.split.map(&:to_i)
  break if (radius + n).zero?
  
  ufos = n.times.map {Ufo.new(*gets.split.map(&:to_i))}
  count = 0
  
  loop do
    ufos.each(&:move)
    ufos.sort_by! {|u| u.dist}
    
    ufos1 = ufos.select {|u| u.dist <= radius}
    count += ufos1.size
    ufos -= ufos1
    break if ufos.size.zero?
    
    min = ufos.shift
      
    ufos = ufos.reject do |u|
      r = (min.y * u.x - min.x * u.y).abs / min.dist
      r < u.r &&
          u.dist * (min.ex * u.ex + min.ey * u.ey) +
             Math.sqrt(u.r ** 2 - r ** 2) > radius
    end
    break if ufos.size.zero?
  end
  
  puts count
end

これむずかしすぎでしょう。検索して初めてわかった。
 

0205 Rock, Paper, Scissors

until (a = gets.to_i).zero?
  given = [a] + 4.times.map {gets.to_i}
  
  hands = given.uniq
  if hands.size == 3 || hands.size == 1
    result = [3] * 5
  else
    point = (hands[0] - hands[1]) % 3 == 2 ? 1 : 2
    result = given.map do |hand|
      hand == hands[0] ? point : 3 - point
    end
  end
  
  puts result
end

突然簡単に。
 

0206 Next Trip

until (l = gets.to_i).zero?
  data = 12.times.map {gets.split.map(&:to_i)}
  month_number = "NA"
  
  acc = 0
  data.each_with_index do |mn, i|
    acc += mn[0] - mn[1]
    if acc >= l
      month_number = i + 1
      break
    end
  end
  
  puts month_number
end

簡単。
 

0207 Block

Position = Struct.new(:x, :y)

loop do
  w, h = gets.split.map(&:to_i)
  break if (w + h).zero?
  field = Array.new(h + 1) {Array.new(w + 1, 0)}
  
  xs, ys = gets.split.map(&:to_i)
  xg, yg = gets.split.map(&:to_i)
  start = Position.new(xs, ys)
  
  gets.to_i.times do
    color, direction, x, y = gets.split.map(&:to_i)
    width, height = direction.zero? ? [4, 2] : [2, 4]
    height.times do |h1|
      width.times {|w1| field[y + h1][x + w1] = color}
    end
  end
  
  start_color = field[ys][xs]
  field[ys][xs] = 100
  stack = [start]
  result = "NG"
  
  while (po = stack.shift)
    if po.x == xg && po.y == yg
      result = "OK"
      break
    else
      [[1, 0], [0, -1], [-1, 0], [0, 1]].each do |dx, dy|
        next_x, next_y = po.x + dx, po.y + dy
        next if next_x <= 0 || next_x > w || next_y <= 0 || next_y > h
        if field[next_y][next_x] == start_color
          stack << Position.new(next_x, next_y)
          field[next_y][next_x] = 100
        end
      end
    end
  end
  
  puts result
end

幅優先探索。簡単。
 

0208 Room Numbers of a Hospital

Table = [0, 1, 2, 3, 3, 4 ,4, 5, 6, 7]

def func(num)
  num1 = num.to_s.chars
  result = 0
  loop do
    top = num1.shift.to_i
    digits = num1.size
    result += Table[top] * 8 ** digits
    break if digits.zero?
  end
  result
end

until (num = gets.to_i).zero?
  puts (1..9358757000).bsearch {|i| num <= func(i)}
end

何かダメ。検索してみる。

Table = [0, 1, 2, 3, 5, 7, 8, 9]

def f(n)
  f(n / 8) if n >= 8
  print Table[n % 8]
end

until (num = gets.to_i).zero?
  f(num)
  puts
end

世の中にはかしこい人がいますな。
 

0209 Scene in a Picture

loop do
  n, m = gets.split.map(&:to_i)
  break if (n + m).zero?
  
  scene   = n.times.map {gets.split.map(&:to_i)}
  picture = m.times.map {gets.split.map(&:to_i)}
  
  pictures = []
  4.times do
    pictures << picture 
    picture = picture.reverse.transpose    #右回転
  end
  
  check = ->{
    result = []
    (0..n - m).each do |y|
      (0..n - m).each do |x|
        pictures.each do |pic|
          catch :jump {
            nx = ny = nil
            m.times do |dy|
              m.times do |dx|
                next if pic[dy][dx] == -1
                nx, ny = x + dx, y + dy unless nx
                throw :jump if pic[dy][dx] != scene[y + dy][x + dx]
              end
            end
            result << [nx + 1, ny + 1]
          }
        end
      end
      return result unless result.empty?
    end
    "NA"
  }
  result = check.()
  
  if result != "NA"
    result = result.sort! {|a, b| (a[1] <=> b[1]).nonzero? || a[0] <=> b[0]}[0].join(" ")
  end
  puts result
end

凡庸なコード。でも Ruby では三人しか出来ていないね。