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 というのは気づかなかった。これは勉強になった。

AOJ(問題集)6

AIZU ONLINE JUDGE: Programming Challenge
 

0051 Differential II

$<.readlines.drop(1).map {|a| a.chomp.chars.map(&:to_i).sort}.each do |ary|
  puts ary.reverse.join.to_i - ary.join.to_i
end

 

0052 Factorial II

until (n = $<.gets.to_i).zero?
  five = n / 5
  f = ->(x) {
    return five if n < x
    five += n / x
    f.(x * 5)
  }
  puts f.(25)
end

これはちょっと考えました。
 

0053 Sum of Prime Numbers

require 'prime'

nums = []
h = {}
until (i = $<.gets.to_i).zero?
  nums << i
  h[i] = 0
end
max_num = nums.max

sum = 0
Prime.each.with_index do |prime, i|
  break if i + 1 > max_num
  sum += prime
  h[i + 1] = sum if h[i + 1]
end
nums.each {|j| puts h[j]}

与えられた値が重複しているのだな。結構いい成績だった。
 

0054 Sum of Nth decimal places

$<.readlines.map {|a| a.split.map(&:to_i)}.each do |a, b, n|
  f = ->(x, i = 1, sum = 0) {
    return sum if i > n or x.zero?
    f.(x * 10 % b, i + 1, sum + x * 10 / b)
  }
  puts f.(a % b)
end

 

0055 Sequence

$<.readlines.map(&:to_f).each do |x|
  sum = 0
  solve = ->(n, i = 1) {
    sum += n
    return sum if i == 10
    (i + 1).even? ? solve.(n * 2.0, i + 1) : solve.(n / 3.0, i + 1)
  }
  puts solve.(x)
end

 

0056 Goldbach's Conjecture

require 'prime'

h = {}
primes = Prime.each(50000).to_a
primes.each {|pr| h[pr] = true}
until (n = $<.gets.to_i).zero?
  co = 0
  if n.odd?
    co = 1 if h[n - 2]
  else
    primes.each do |pr|
      break if pr > n / 2
      co += 1 if h[n - pr]
    end 
  end
  puts co
end

何か変なミスをしていた。何とタイムは挑戦者中最速。
 

0057 The Number of Area

$<.readlines.map(&:to_i).each do |n|
  puts 2 + (n - 1) * (n + 2) / 2
end

 

0058 Orthogonal

$<.readlines.map {|l| l.split.map(&:to_r)}.each do |xa, ya, xb, yb, xc, yc, xd, yd|
  x1, y1 = xb - xa, yb - ya
  x2, y2 = xd - xc, yd - yc
  puts x1 * x2 + y1 * y2 == 0 ? "YES" : "NO"
end

 

0059 Intersection of Rectangles

while (st = $<.gets)
  xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2 = st.split.map(&:to_f)
  if !(xa1 > xb2 or xa2 < xb1 or ya1 > yb2 or ya2 < yb1)
    puts "YES"
  else
    puts "NO"
  end
end

こんなに簡単なものが条件を見落としていた…。
 

0060 Card Game

$<.readlines.map {|a| a.split.map(&:to_i)}.each do |my1, my2, opp1|
  deck = [*1..10] - [my1, my2, opp1]
  limit = 20 - (my1 + my2)
  p = 0
  deck.each do |opp2|  
    n = (deck - [opp2]).take_while {|i| i <= limit}.size
    p += 1/7r * Rational(n, 6)
  end
  puts p >= 1/2r ? "YES" : "NO"
end

AOJ(問題集)5

AIZU ONLINE JUDGE: Programming Challenge
 

0041 Expression

def solve(ary)
  if ary.size <= 1
    return ary[0] if eval(ary[0]) == 10
  else
    idxs = [*0...ary.size]
    idxs.combination(2) do |i, j|
      a, b = ary[i], ary[j]
      nxt = (idxs - [i, j]).map{|x| ary[x]}
      ["(#{a} + #{b})", "(#{a} - #{b})", "(#{b} - #{a})", "#{a} * #{b}"].each do |st|
        result = solve(nxt + [st])
        return result if result
      end
    end
  end
  nil
end

until (given = $<.gets.chomp.split) == ["0", "0", "0", "0"]
  ans = solve(given)
  puts ans ? ans : 0
end

自信作(笑)。
 

0042 A Thief

i = 1
until (furoshiki = $<.gets.to_i).zero?
  table = {0 => 0}
  Array.new($<.gets.to_i) {$<.gets.split(",").map(&:to_i)}.each do |v, w|
    h = Hash.new(0)
    table.each {|key, value| h[key + w] = table[key] + v if key + w <= furoshiki}
    table.merge!(h) {|k, v1, v2| [v1, v2].max}
  end
  m = table.values.max
  result = table.select {|k, v| v == m}.sort {|a, b| a[0] <=> b[0]}.first
  puts "Case #{i}:", result[1], result[0]
  i += 1
end

典型的な動的計画法
 

0043 Puzzle

def check(table)
  result = 0
  if table.sum <= 2
    result = 1 if table.find {|x| x == 2}
  else
    (1..7).each do |i|
      tmp = table.dup
      if tmp[i].nonzero? and tmp[i + 1].nonzero? and tmp[i + 2].nonzero?
        3.times {|x| tmp[i + x] -= 1}
        result += check(tmp)
        return 1 if result.nonzero?
      end
      (1..9).each do |j|
        tmp1 = table.dup
        if tmp1[j] >= 3
          tmp1[j] -= 3
          result += check(tmp1)
          return 1 if result.nonzero?
        end
      end
    end
  end
  result
end

$<.readlines.map(&:chomp).map {|n| n.chars.map(&:to_i)}.each do |data|
  table = Array.new(10, 0)
  data.each {|i| table[i] += 1}
  result = (1..9).map do |i|
    added = table.dup
    added[i] += 1
    next if added[i] > 4
    check(added).nonzero? ? i : nil
  end.compact
  puts result.empty? ? 0 : result.join(" ")
end

3.09秒かかった。

他の人の回答を見ていて気づいたが、check() メソッド内のループはネストさせる必要がない。書き直すと

def check(table)
  result = 0
  if table.sum <= 2
    table.find {|x| x == 2}
  else
    (1..7).each do |i|
      tmp = table.dup
      if tmp[i].nonzero? and tmp[i + 1].nonzero? and tmp[i + 2].nonzero?
        3.times {|x| tmp[i + x] -= 1}
        return true if check(tmp)
      end
    end
    (1..9).each do |j|
      tmp1 = table.dup
      if tmp1[j] >= 3
        tmp1[j] -= 3
        return true if check(tmp1)
      end
    end
    false
  end
end

$<.readlines.map(&:chomp).map {|n| n.chars.map(&:to_i)}.each do |data|
  table = Array.new(10, 0)
  data.each {|i| table[i] += 1}
  result = (1..9).map do |i|
    added = table.dup
    added[i] += 1
    next if added[i] > 4
    check(added) ? i : nil
  end.compact
  puts result.empty? ? 0 : result.join(" ")
end

これで 0.10秒。充分速い。
 

0044 Prime Number II

require 'prime'

$<.readlines.map(&:to_i).each do |n|
 a = (n - 1).step(0, -1) {|i| break i if Prime.prime?(i)}
 b = (n + 1).step {|i| break i if Prime.prime?(i)}
 puts "#{a} #{b}"
end

標準添付ライブラリを使うというインチキ。
 

0045 Sum and Average

all_price = amount = i = 0
$<.readlines.map {|x| x.split(",").map(&:to_i)}.each do |price, n|
  all_price += price * n
  amount += n
  i += 1
end
puts all_price
puts (amount / i.to_f).round

 

0046 Differential

max, min = 0, Float::INFINITY
$<.readlines.map(&:to_f).each do |h|
  max = h if h > max
  min = h if h < min
end
printf "%.1f\n", max - min

 

0047 Cup Game

place = "A"
$<.readlines.map {|x| x.chomp.split(",")}.each do |a, b|
  if place == a
    place = b
  elsif place == b
    place = a
  end
end
puts place

 

0048 Class

table = [["light fly", 0, 48.0], ["fly", 48.0, 51.0], ["bantam", 51.0, 54.0],
         ["feather", 54.0, 57.0], ["light", 57.0, 60.0],
         ["light welter", 60.0, 64.0], ["welter", 64.0, 69.0],
         ["light middle", 69.0, 75.0], ["middle", 75.0, 81.0],
         ["light heavy", 81.0, 91.0], ["heavy", 91.0, Float::INFINITY]]
$<.readlines.map(&:to_f).each do |w|
  table.each {|name, bottom, top| puts name if bottom < w and w <= top}
end

 

0049 Blood Groups

table = {"A"=>0, "B"=>1, "AB"=>2, "O"=>3}
nums = Array.new(4, 0)
$<.readlines.map {|a| a.chomp.split(",").last}.each do |type|
  nums[table[type]] += 1
end
puts nums

 

0050 Apple and Peach

st = $<.gets.chomp
po = 0
result = ""
while po < st.length
  case st[po, 5]
  when "apple"
    result += "peach"
    po += 5
  when "peach"
    result += "apple"
    po += 5
  else
    result += st[po]
    po += 1
  end
end
puts result

Windows フリーゲーム「Almagest」を Linux で遊ぶ

20181210015655

ゲーム HP。
Almagest -Overture-
以下よりダウンロードする。
「Almagest -Overture-」ターン制SFシミュレーションゲーム - 窓の杜
 
lzh ファイルは Linux ではそのままでは解答できないので、ここでは lhasa を入れてみる。

$ sudo apt install lhasa

これで解凍できる。ver 3.04 の差分フォルダもダウンロードした場合は、手作業で差分ファイルを差し替える(参照)。

動かすためには Wine が必要です。実行はふつうに

$ wine Almagest-1.exe

でよい。特にインストールとかはされない。
 
Linux Mint 19, wine-3.0 で動作確認。
 
※参考
Almagest -Overture- - Wikipedia
Almagestとは (アルマゲストとは) [単語記事] - ニコニコ大百科
Almagest Wiki - Front Page

はがきデザインキット2019 を Ubuntu 18.10 にインストール

20181209212302

以前に郵便局の「はがきデザインキット」を Ubuntu にインストールする記事(参照)を書いたのですが、その手順が古くなったので再挑戦です。

まずは Wine が使えるか調べます。「Wine」とは LinuxWindows のソフトを動かすためのアプリです。

$ wine --version

Command 'wine' not found, but can be installed with:

sudo apt install wine            
sudo apt install wine-development

というわけで入っていないので、表示されたとおりにやります。

$ sudo apt update
$ sudo apt install wine-stable

あとで必要なのでやっておきます。(※後記:たぶん samba と winbind のインストールは必要ない気がします。飛ばして下さい。)

$ sudo apt install samba
$ sudo apt install winbind

Wine がインストールされているか確認します。

$ wine --version
wine-3.18 (Ubuntu 3.18-2)

このままだと文字化けします。なので

$ sudo apt install winetricks
$ winetricks allfonts

を実行します(参照)。終了後、

$ winecfg

で文字化けがなければ OK です。これで Wine のインストールが完了しました。


つぎに Abode Air をインストールします。ここから「手順1」は「Windows」、「手順2」は「Adobe AIR 32.0 for Win64」を選択し、「今すぐダウンロード」をクリックします。念のため

$ sudo apt install mono-complete

をしておいてから、ダウンロードした exe ファイルを

$ wine AdobeAIRInstaller.exe

で実行。インストーラーがうまく働いていないように見えますが、フリーズ後に [Ctrl] + [C] をして(うまくいかなければプロセスを終了させる)上記コマンドを再実行すると、インストール済の表示が出れば OK です。 (追記:インストーラーのプロセスはすべて必ず終了させて下さい。)


最後にここから「はがきデザインキット」をダウンロード。zip ファイルを解凍して design_kit.air を右クリック、[他のアプリケーションで開く]→[Adobe AIR Application Installer] を選択して実行すると、インストールされる筈です。お疲れ様でした!
 

追記(2020年度版へのアップグレード)

2019年度版から2020年度版へアップグレードする場合、ソフトで用意された方法ではうまくアップグレードできないかも知れません。その場合は2020年度版をダウンロードし、上の手順で design_kit.air を再インストールしてみて下さい。これでふつうにバージョンアップできると思います。

AOJ(問題集)4

AIZU ONLINE JUDGE: Programming Challenge
 

0031 Weight

def measure(object, weight, result = [])
  return result.join(" ") if weight.zero?
  result.unshift(weight) if (object / weight).nonzero?
  measure(object % weight, weight / 2, result)
end

$<.readlines.map(&:to_i).each {|ob| puts measure(ob, 512)}

 

0032 Plastic Board

rectangle = lozenge = 0

$<.readlines.map {|l| l.split(",").map(&:to_i)}.each do |a, b, c|
  rectangle += 1 if a * a + b * b == c * c
  lozenge   += 1 if a == b
end
puts rectangle, lozenge

 

0033 Ball

def try(balls, b ,c)
  return 1 if balls.empty?
  nxt = balls.first
  bl = balls.drop(1)
  if nxt > b and nxt > c
    try(bl, nxt, c) + try(bl, b, nxt)
  elsif nxt > b
    try(bl, nxt, c)
  elsif nxt > c
    try(bl, b, nxt)
  else
    0
  end
end

$<.gets.to_i.times do
  balls = $<.gets.split.map(&:to_i)
  puts try(balls.drop(1), balls.first, 0).nonzero? ? "YES" : "NO"
end

読み込みに readlines を使ったらエラーが出る。gets を使ったコードに替えたら正解になった。非本質的なところで延々と悩まされた。
 

0034 Railway Lines

$<.readlines.map {|l| l.split(",").map(&:to_i)}.each do |given|
  ls = given.first(10)
  v1, v2 = given.last(2)
  s = Rational(v1 * ls.sum, v1 + v2)
  l = 0
  ls.each_with_index do |ln, i|
    next if s > (l += ln)
    puts i + 1
    break
  end
end

 

0035 Is it Convex?

$<.readlines.map {|l| l.split(",").map(&:to_r)}.each do |given|
  f = (given + given.first(4)).each_slice(2).each_cons(3).map do |a, b, c|
    (c[0] - a[0]) * (b[1] - a[1]) - (c[1] - a[1]) * (b[0] - a[0]) > 0
  end
  puts((f.all? or f.none?) ? "YES" : "NO")
end

 

0036 A Figure on Surface

L = 8
table = [["11", "11"], ["1", "1", "1", "1"], ["1111"], ["01", "11", "10"],
         ["110", "011"], ["10", "11", "01"], ["011", "110"]]
table.map! {|pt| [pt.size, l = pt.first.length, pt.join("0" * (L - l))]}

$<.readlines.chunk {|l| !!l.match(/^\d/) || nil}
  .map {|a| a.last.map(&:chomp).join}.each do |field|
  table.each_with_index do |ary, k|
    y, n, pt = ary
    (L + 1 - y).times do |i|
      (L + 1 - n).times do |j|
        puts "ABCDEFG"[k] if field[i * L + j, pt.length] == pt
      end
    end
  end
end

 

0037 Path on a Grid

given = $<.readlines.map {|l| l.chomp.chars.map(&:to_i)}
yoko = 0.step(8, 2).map {|i| given[i]}
tate = 1.step(8, 2).map {|i| given[i]}.transpose
dirs = "LRUD"
go = nil
route = ""
f = false

get_yoko = ->(x, y, dir) {
  if dir == 0
    if tate[x][y] == 1
      go.(x, y, 3)
    elsif x > 0 and yoko[y][x - 1] == 1
      go.(x, y, 0)
    elsif y > 0 and tate[x][y - 1] == 1
      go.(x, y, 2)
    else
     go.(x, y, 1)
    end
  else
    if y > 0 and tate[x][y - 1] == 1
      go.(x, y, 2)
    elsif x <= 3 and yoko[y][x] == 1
      go.(x, y, 1)
    elsif tate[x][y] == 1
      go.(x, y, 3)
    else
      go.(x, y, 0)
    end
  end
}

get_tate = ->(x, y, dir) {
  if dir == 2
    if x > 0 and yoko[y][x - 1] == 1
      go.(x, y, 0)
    elsif y > 0 and tate[x][y - 1] == 1
      go.(x, y, 2)
    elsif yoko[y][x] == 1
      go.(x, y, 1)
    else
      go.(x, y, 3)
    end
  else
    if x <= 3 and yoko[y][x] == 1
      go.(x, y, 1)
    elsif y <= 3 and tate[x][y] == 1
      go.(x, y, 3)
    elsif x > 0 and yoko[y][x - 1] == 1
      go.(x, y, 0)
    else
      go.(x, y, 2)
    end
  end
}

go = ->(x, y, dir) {
  return if x.zero? and y.zero? and f
  f = true
  route += dirs[dir]
  if dir <= 1
    get_yoko.(x + dir * 2 - 1, y, dir)
  else
    get_tate.(x, y + dir * 2 - 5, dir)
  end
}

go.(0, 0, 1)
puts route

何だか全然よくないコード。もっと考えたい。
 

0038 Poker Hand

def same_numbers(ary, result = [])
  return result.sort if ary.empty?
  a = ary.first
  co = ary.count(a)
  result << co if co > 1
  same_numbers(ary - [a], result)
end

$<.readlines.map {|l| l.split(",").map(&:to_i)}.each do |hand|
  puts case same_numbers(hand)
  when [4]    then "four card"
  when [2, 3] then "full house"
  when [3]    then "three card"
  when [2, 2] then "two pair"
  when [2]    then "one pair"
  else
    hand.sort!
    if hand.sum == (hand.first + 2) * 5 or hand == [1, 10, 11, 12, 13]
      "straight"
    else
      "null"
    end
  end
end

 

0039 Roman Figure

table = {I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000, nil => 0}

count = ->(roman, number = 0) {
  return number if roman.empty?
  a, b = table[roman.first], table[roman[1]]
  number += if a >= b
    r = roman.drop(1)
    a
  else
    r = roman.drop(2)
    b - a
  end
  count.(r, number)
}

$<.readlines.map(&:chomp).each {|roman| puts count.(roman.chars.map(&:to_sym))}

 

0040 Affine Cipher

def decode(word, a, b)
  g = (0..25).map {|i| [[*"a".."z"][(a * i + b) % 26], i]}.to_h
  word.chars.map {|st| (g[st] + 97).chr}.join
end

e = Enumerator.new do |y|
  generate = ->(n) {
    a, b = n, 0
    n.times do
      y << [a, b]
      a -= 1
      b += 1
    end
    generate.(n + 1)
  }
  generate.(1)
end

$<.readlines.drop(1).map {|a| a.split}.each do |sentence|
  loop do
    a, b = e.next
    dsr = (1..a).select {|i| (a % i).zero?}
    next if dsr.include?(2) or dsr.include?(13) or dsr.include?(26)
    f = sentence.select {|w| w.length == 4}.map do |word|
      st = decode(word, a, b)
      st == "that" or st == "this"
    end.any?
    next unless f
    puts sentence.map {|word| decode(word, a, b)}.join(" ")
    break
  end
end

アフィン暗号の解読にはここを参考にした。

うーん、あんまりひどいタイムなので、他の人のコードを参考にした。すごい。

cl = [*"a".."z"]
table_a = (1..26).select {|i| 26.gcd(i) == 1}
b = nil

$<.readlines.drop(1).each do |str|
  a = table_a.find do |i|
    b = (0..26).find do |j|
      nominees = str.split.select {|w| w.length == 4}
      ["that", "this"].map do |x|
        nominees.include?(x.chars.map {|c| cl[(i * cl.index(c) + j) % 26]}.join)
      end.any?
    end
  end
  table = (0..25).map {|i| (a * i + b) % 26}
  puts str.chomp.chars.map {|c| (c == " ") ? c : cl[table.index(cl.index(c))]}.join
end

暗黙の Proc化(Ruby)

[1] pry(main)> (1..4).map {|i| i + 3}
=> [4, 5, 6, 7]

これと

[2] pry(main)> (1..4).map(&->(i) {i + 3})
=> [4, 5, 6, 7]

は同じ。引数での & は Proc をブロックに変換するから。(正確にはさらにそれを暗黙に .call() している。)


では、

[3] pry(main)> (1..4).map(&3)
TypeError: wrong argument type Integer (expected Proc)

はもちろんエラーになるよね。でも、この &3 のとき、3 は Proc でないので、暗黙に 3 を to_proc しようとしている。それを示してみよう。

Integer クラスにこうモンキーパッチしてみる。

[4] pry(main)> class Integer
[4] pry(main)*   def to_proc  
[4] pry(main)*     ->(i){i + self}    
[4] pry(main)*   end    
[4] pry(main)* end  
=> :to_proc

Integer を Proc化するのである。すると、

[5] pry(main)> (1..4).map(&3)
=> [4, 5, 6, 7]

おお、先ほどと同じだ。つまり、ここでは暗黙に 3 を to_proc しているのである。うーん、すごいですなあ。

つまり、オブジェクトに to_proc を定義しておくと、このような遊び(?)ができるわけです。可読性がなにですが。

AOJ(問題集)3

AIZU ONLINE JUDGE: Programming Challenge
 

0021 Parallelism

$<.readlines.drop(1).map {|a| a.split.map(&:to_r)}.each do |x1, y1, x2, y2, x3, y3, x4, y4|
  puts((x2 - x1) * (y4 - y3) == (x4 - x3) * (y2 - y1) ? "YES" : "NO")
end

単純な問題なのに、自分の解き方ではどうしてもダメだったので、他の人のを参考に。to_f じゃなくて to_r がミソなのかなあ。
 

0022 Maximum Sum Sequence

while (n = $<.gets.to_i).nonzero?
  given = (1..n).map {$<.gets.to_i}
  max = -Float::INFINITY
  n.times do |i|
    sum = 0
    (i...n).each do |j|
      sum += given[j]
      max = sum if sum > max
    end
  end
  puts max
end

 

0023 Circles Intersection

$<.readlines.drop(1).map {|a| a.split.map(&:to_f)}.each do |xa, ya, ra, xb, yb, rb|
  l = Math.sqrt((xb - xa) ** 2 + (yb - ya) ** 2)
  r = ra - rb
  puts case
       when l < r  then 2
       when l < -r then -2
       when l > ra + rb then 0
       else 1
       end
end

 

0024 Physical Experiments

$<.readlines.map(&:to_r).each do |v|
  puts((1 + 1/98r * v ** 2).ceil)
end

 

0025 Hit and Blow

$<.readlines.map {|a| a.split.map(&:to_i)}.each_slice(2) do |a, b|
  a1, b1 = [], []
  hit = 0
  a.each_index do |i|
    if a[i] == b[i]
      hit += 1
    else
      a1 << a[i]
      b1 << b[i]
    end
  end
  blow = a1.size - (a1 - b1).size
  puts "#{hit} #{blow}"
end

 

0026 Dropping Ink

L = 10
table = [a = [[0, 0], [0, -1], [1, 0], [0, 1], [-1, 0]],
         b = a + [[1, -1], [1, 1], [-1, 1], [-1, -1]],
         b + [[0, -2], [2, 0], [0, 2], [-2, 0]]]
field = Array.new(L) {Array.new(L, 0)}

set = ->(x, y) {
  return if x < 0 or x >= L or y < 0 or y >= L
  field[y][x] += 1
}

$<.readlines.map {|l| l.split(",").map(&:to_i)}.each do |x, y, s|
  table[s - 1].each {|dx, dy| set.(x + dx, y + dy)}
end
f = field.flatten
puts f.count(0)
puts f.max

 

0027 What day is today?

require 'date'
table = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)

loop do
  month, day = $<.gets.split.map(&:to_i)
  break if month.zero?
  puts table[Date.new(2004, month, day).wday]
end

 

0028 Mode Value

field = Array.new(101, 0)
$<.readlines.map(&:to_i).each {|n| field[n] += 1}
m = field.max
field.each_with_index {|n, i| puts i if m == n}

 

0029 English Sentence

counts = Hash.new(0)
max = [0, nil]

$<.gets.split.map(&:downcase).each do |word|
  counts[word] += 1
  l = word.length
  max = [l, word] if l > max.first
end
h = counts.invert
puts "#{h[h.keys.max]} #{max.last}"

 

0030 Sum of Integers

loop do
  n, s = $<.gets.split.map(&:to_i)
  break if n.zero? and s.zero?
  puts [*0..9].combination(n).map {|numbers| numbers.sum == s}.count(true)
end