Rubyで「分野別 初中級者が解くべき過去問精選100問」を解く

レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】 - Qiita
 
全探索:全列挙

ITP1_7_B - How Many Ways?

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_7_B&lang=ja

loop do
  n, x = gets.split.map(&:to_i)
  break if (n + x).zero?
  
  count = 0
  [*1..n].combination(3) do |given|
    count += 1 if given.inject(:+) == x
  end
  
  puts count
end

 

AtCoder Beginner Contest 106 B - 105

https://atcoder.jp/contests/abc106/tasks/abc106_b

n = gets.to_i

puts 1.step(n, 2).map {|i|
       (1..i).select {|x| (i % x).zero?}.size == 8
     }.count(true)

 

AtCoder Beginner Contest 122 B - ATCoder

https://atcoder.jp/contests/abc122/tasks/abc122_b

str = gets.chomp
result = str.split(/[^ACGT]/).map(&:size).max
puts result || 0

 
全列挙で解いてみる。

s = gets.chomp

max = 0

doit = ->(str) {
  return if str.empty?
  (str.length).downto(1) do |l|
    next if /[^ACGT]/.match(str[0, l])
    max = l if l > max
  end
  doit.(str[1..-1])
}
doit.(s)

puts max

 

パ研杯2019 C - カラオケ

https://atcoder.jp/contests/pakencamp-2019-day3/tasks/pakencamp_2019_day3_c

n, m = gets.split.map(&:to_i)
table = n.times.map {gets.split.map(&:to_i)}

puts [*0...m].combination(2).map {|t1, t2|
       n.times
        .map {|student| [table[student][t1], table[student][t2]].max}
        .inject(:+)
     }.max

 
全探索:工夫して通り数を減らす全列挙

AtCoder Beginner Contest 095 C - Half and Half

https://atcoder.jp/contests/abc095/tasks/arc096_a

a, b, ab, x, y = gets.split.map(&:to_i)

z = [x, y].max * 2
puts 0.step(z, 2).map {|n_ab|
       n_a = [x - n_ab / 2, 0].max
       n_b = [y - n_ab / 2, 0].max
       a * n_a + b * n_b + ab * n_ab
     }.min

 

三井住友信託銀行プログラミングコンテスト 2019 D - Lucky PIN

https://atcoder.jp/contests/sumitrust2019/tasks/sumitb2019_d

require "set"

gets
s = gets.chomp.chars

pool = Set.new
s.combination(3) {|code| pool << code.join}

puts pool.size

予想どおり TLE。

考え直す。

gets.to_i
str = gets.chomp

is_included = ->(i) {
  pointer = 0
  table = sprintf("%03d", i).chars
  str.each_char do |c|
    if c == table[pointer]
      pointer += 1
      return true if pointer == 3
    end
  end
  false
}

puts (0..999).map {|i| is_included.(i)}.count(true)

TLE。

考え直す。

n = gets.to_i
str = gets.chomp

check = Array.new(1000, 0)

0.upto(n - 3) do |i|
  (i + 1).upto(n - 2) do |j|
    (j + 1).upto(n - 1) do |k|
      num = (str[i] + str[j] + str[k]).to_i
      check[num] = 1
    end
  end
end

puts check.inject(:+)

TLE。

他の人の答えを見た。すごい工夫だな。たった 8ms。

n = gets.to_i
s = gets.chomp

puts [*"0".."9"].repeated_permutation(3).count {|i, j, k|
       (a = s.index(i, 0)) && (b = s.index(j, a + 1)) && s.index(k, b + 1)
     }

でも、よく考えたらこれ、is_included を使う解法とアルゴリズムは一緒じゃないか。Ruby の組み込みを使うかどうかだけだな。
 

JOI 2007 本選 3 - 最古の遺跡

n = gets.to_i
pillars = n.times.map {gets.split.map(&:to_i)}

max = 0

pillars.combination(2) do |p1, p2|
  vy = p2[0] - p1[0]
  vx = p1[1] - p2[1]
  calc = ->(x, y) {
    return false unless pillars.include?([p1[0] + x, p1[1] + y])
    return false unless pillars.include?([p2[0] + x, p2[1] + y])
    x * x + y * y
  }
  area = calc.(vx, vy)
  max = area if area && area > max
  area = calc.(-vx, -vy)
  max = area if area && area > max
end

puts max

TLE。Ruby では正解者なし。

高速化。

n = gets.to_i
pillars_str = n.times.map {gets.chomp}

max = 0
pillars = pillars_str.map {|pl| pl.split.map(&:to_i)}

pillars.combination(2) do |p1, p2|
  vy = p2[0] - p1[0]
  vx = p1[1] - p2[1]
  calc = ->(x, y) {
    return false unless pillars_str.include?("#{p1[0] + x} #{p1[1] + y}")
    return false unless pillars_str.include?("#{p2[0] + x} #{p2[1] + y}")
    x * x + y * y
  }
  area = calc.(vx, vy)
  max = area if area && area > max
  area = calc.(-vx, -vy)
  max = area if area && area > max
end

puts max

3/10 AC だったのが、6/10 AC に改善された。でもやはり TLE。
lambda をメソッド呼び出しに変えてもさほど改善しなかった。