LoginSignup
1
1

More than 5 years have passed since last update.

第24回オフラインリアルタイムどう書く「多段階選抜」の rubyによる実装

Last updated at Posted at 2014-08-03

問題 http://nabetani.sakura.ne.jp/hena/ord24eliseq/
解答リンク集 http://qiita.com/Nabetani/items/1c83005a854d2c6cbb69
イベント http://yhpg.doorkeeper.jp/events/13159

当日お見せした実装。

def is_sq(b)
  (b**0.5).round**2==b
end

def is_cu(b)
  (b**(1r/3)).round**3==b
end

module Enumerable
  def prepend( a )
    if block_given?
      a.each{ |e| yield e }
      self.each{ |e| yield e }
    else
      to_enum( :prepend, a )
    end
  end
end

def remove_before( seq )
  seq.each_cons(2).select{ |i| ! yield i[1] }.map{ |i| i[0] }
end

def remove_after( seq )
  seq.each_cons(2).select{ |i| ! yield i[0] }.map{ |i| i[1] }.prepend( seq.first(1) )
end

def solve_core( s )
  seq=loop.lazy.with_index(1).map{ |*i| i.last }
  s.chars.each do |cmd|
    seq=case cmd
    when "s"; remove_before( seq ){ |n| is_sq(n) }
    when "S"; remove_after( seq ){ |n| is_sq(n) }
    when "c"; remove_before( seq ){ |n| is_cu(n) }
    when "C"; remove_after( seq ){ |n| is_cu(n) }
    when "h"; seq.drop(100)
    when /[2-9]/
      seq.each_slice(cmd.to_i).flat_map{ |i| i[0..-2] }
    else
      raise "unexpected cmd : #{cmd} in #{s}"
    end
  end
  seq
end

def solve( s, len=10 )
  solve_core( s ).first(len).to_a.join(",")
end

DATA.map{ |line|
  id, src, expected = line.split(/\s+/ )
  actual = solve( src )
  ok = actual == expected
  puts "***NG*** : %s %s->%s ( %s )" % [ id, src, actual, expected ] unless ok
  ok
}.all?.tap{ |r| puts ( r ? "ok" : "NG" ) }

__END__
0 ss6cc24S  1,9,21,30,33,37,42,44,49,56
1 h 101,102,103,104,105,106,107,108,109,110
2 hh  201,202,203,204,205,206,207,208,209,210
3 hhh 301,302,303,304,305,306,307,308,309,310
50  23h465Ssc9CchC  1027,1033,1045,1047,1057,1069,1071,1075,1081,1093

いつも通り、テストデータの大半は省略。

たぶん、遅延評価を前提に無限リストを作って、それをこねると出来上がる、というのが順当な実装だと思う。

遅延評価がない処理系だと固定長で用意していくんだと思うけど、ruby には lazy があるので大丈夫。

実装で手間取ったのは
 「無限リストの前に要素を1つ追加する」
という処理を書く部分。
当初はどうせあるだろうと思って調べたんだけど実は存在せず、実装するはめになった。
普通に [1]+lazy_enum という感じで加算できていいと思うんだけど。

当日罠として機能した三乗根の計算は

(b**(1r/3)).round**3==b

という風にして浮動小数点の計算誤差を回避したんだけど、ruby には Math.cbrt があることを教えていただいた。

Math.cbrt(100**3) #=> 100.0
(100**3)**(1r/3) #=> 99.99999999999997

Math.cbrt(100.00000000000001**3) #=> 100.00000000000001
 (100.00000000000001**3)**(1r/3) #=> 99.99999999999999

という感じ。素晴らしい。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1