Created
April 9, 2019 04:49
-
-
Save yancya/17c3da1b099611964310a04b2d1fa6a9 to your computer and use it in GitHub Desktop.
http://nabetani.sakura.ne.jp/hena/orde32rects/ への yancya の解答
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pg' | |
require 'tapp' | |
def main(str) | |
line_segments = str.split('/').flat_map { |sq| | |
left, top, right, bottom = sq.chars.map { |i36| i36.to_i(36) } | |
[ # ax ay bx by | |
[left, top, right, top ], # top segment | |
[right, top, right, bottom], # right segment | |
[left, bottom, right, bottom], # bottom segment | |
[left, top, left, bottom], # left segment | |
] | |
} | |
loop.with_object(line_segments) do |_, a| | |
new_lines = new_lines(line_segments) | |
break if new_lines.empty? | |
line_segments += new_lines | |
end | |
boxes = build_boxes(line_segments) | |
pure_boxes(boxes).map { |box| | |
top, right, bottom, left = box.map(&:to_i) | |
(bottom-top)*(right-left) | |
}.sort.join(',') | |
end | |
def new_lines(line_segments) | |
values = line_segments. | |
map { |ax, ay, bx, by| "(#{ax}, #{ay}, #{bx}, #{by})"}. | |
yield_self { |tuples| 'VALUES' + tuples.join(', ') } | |
PG.connect.exec(<<~SQL).to_a.map(&:values) | |
WITH line_segments(ax, ay, bx, by) AS (#{values}) | |
SELECT CASE WHEN self.ax < other.ax THEN self.ax ELSE other.ax END AS ax | |
, CASE WHEN self.ay < other.ay THEN self.ay ELSE other.ay END AS ay | |
, CASE WHEN self.bx > other.bx THEN self.bx ELSE other.bx END AS bx | |
, CASE WHEN self.by > other.by THEN self.by ELSE other.by END AS by | |
FROM line_segments AS self | |
JOIN line_segments AS other | |
ON self <> other | |
AND ( | |
( | |
self.ax = self.bx | |
AND (self.ax, self.bx) = (other.ax, other.bx) | |
AND int8range(self.ay, self.by, '[]') && int8range(other.ay, other.by, '[]') | |
) OR ( | |
self.ay = self.by | |
AND (self.ay, self.by) = (other.ay, other.by) | |
AND int8range(self.ax, self.bx, '[]') && int8range(other.ax, other.bx, '[]') | |
) | |
) | |
EXCEPT SELECT * FROM line_segments | |
SQL | |
end | |
def build_boxes(line_segments) | |
values = line_segments. | |
map { |ax, ay, bx, by| "(#{ax}, #{ay}, #{bx}, #{by})"}. | |
yield_self { |tuples| 'VALUES' + tuples.join(', ') } | |
PG.connect.exec(<<~SQL).to_a.map(&:values) | |
WITH line_segments(ax, ay, bx, by) AS (#{values}) | |
, w_lines AS ( | |
SELECT DISTINCT | |
CASE WHEN self.ay < other.ay THEN self.ay ELSE other.ay END AS top -- y 小さい方が TOP | |
, CASE WHEN self.bx < other.bx THEN self.bx ELSE other.bx END AS right -- bx 小さい方が RIGHT | |
, CASE WHEN self.ay > other.ay THEN self.ay ELSE other.ay END AS bottom -- y 大きい方が BOTTOM | |
, CASE WHEN self.ax > other.ax THEN self.ax ELSE other.ax END AS left -- ax 大きい方が LEFT | |
FROM line_segments AS self | |
JOIN line_segments AS other | |
ON self <> other | |
AND self.ay = self.by AND other.ay = other.by -- 横線同士 | |
AND (self.ay, self.by) <> (other.ay, other.by) -- 同じ軸にいない | |
AND int8range(self.ax, self.bx) && int8range(other.ax, other.bx) -- 重なる | |
) | |
, h_lines AS ( | |
SELECT DISTINCT | |
CASE WHEN self.ay > other.ay THEN self.ay ELSE other.ay END AS top -- ay 大きい方が TOP | |
, CASE WHEN self.ax > other.ax THEN self.ax ELSE other.ax END AS right -- x 大きい方が RIGHT | |
, CASE WHEN self.by < other.by THEN self.by ELSE other.by END AS bottom -- by 小さい方が BOTTOM | |
, CASE WHEN self.ax < other.ax THEN self.ax ELSE other.ax END AS left -- x 小さい方が LEFT | |
FROM line_segments AS self | |
JOIN line_segments AS other | |
ON self <> other | |
AND self.ax = self.bx AND other.ax = other.bx -- 横線同士 | |
AND (self.ax, self.bx) <> (other.ax, other.bx) -- 同じ軸にいない | |
AND int8range(self.ay, self.by) && int8range(other.ay, other.by) -- 重なる | |
) | |
SELECT DISTINCT w_lines.top, h_lines.right, w_lines.bottom, h_lines.left | |
FROM w_lines | |
JOIN h_lines | |
ON int8range(w_lines.top, w_lines.bottom, '[]') <@ int8range(h_lines.top, h_lines.bottom, '[]') | |
AND int8range(h_lines.left, h_lines.right, '[]') <@ int8range(w_lines.left, w_lines.right, '[]') | |
SQL | |
end | |
def pure_boxes(boxes) | |
values = boxes.map { |top, right, bottom, left| "(#{top},#{right},#{bottom},#{left})"}. | |
yield_self { |tuples| 'VALUES' + tuples.join(',') } | |
sql = <<~SQL | |
WITH box(top, "right", bottom, "left") AS (#{values}) | |
SELECT self.* | |
FROM box AS self | |
LEFT OUTER JOIN box AS other | |
ON int8range(self.top, self.bottom) && int8range(other.top, other.bottom) | |
AND int8range(self.left, self.right) && int8range(other.left, other.right) | |
AND NOT (int8range(self.top, self.bottom, '[]') <@ int8range(other.top, other.bottom, '[]') | |
AND | |
int8range(self.left, self.right, '[]') <@ int8range(other.left, other.right, '[]')) | |
AND self <> other | |
WHERE other IS NULL | |
SQL | |
PG.connect.exec(sql).to_a.map(&:values) | |
end | |
require 'test-unit' | |
class HogeTest < Test::Unit::TestCase | |
TEST_CASES = DATA.each_line.map { |line| | |
%r[^/\*(?<id>\d+)\*/.*test\((?<act_exp>.*)\)] =~ line | |
act, exp = act_exp.scan(%r["(.+?)"]).flatten | |
[id, [act, exp]] | |
}.to_h | |
data(TEST_CASES) | |
test "hoge" do |(input, expected)| | |
assert { main(input) == expected } | |
end | |
end | |
__END__ | |
/*0*/ test( "43gw/d7qq/mlop", "8,57" ); | |
/*1*/ test( "034a", "28" ); | |
/*2*/ test( "06qr/8pnq", "15" ); | |
/*3*/ test( "c1th/b2qy", "210" ); | |
/*4*/ test( "c8wz/gbsg/i0xd", "20" ); | |
/*5*/ test( "97uv/2g5x/eihv", "39,51" ); | |
/*6*/ test( "254d/2jvu/mjvu", "16,99,220" ); | |
/*7*/ test( "ggiu/ggzu/g5ig", "22,28,238" ); | |
/*8*/ test( "jbnc/i7xe/i7je/icje", "2,4,5" ); | |
/*9*/ test( "3cey/0fzo", "27,33,99,110,189" ); | |
/*10*/ test( "00ab/p0zd/0ofz/87rs", "8,12,28" ); | |
/*11*/ test( "1dsy/2d9s/2s9y", "21,42,105,399" ); | |
/*12*/ test( "28db/d0lm/d1i8/l0w5", "33,35,55" ); | |
/*13*/ test( "3aen/4fir/1lev", "2,20,40,48,60" ); | |
/*14*/ test( "j7ou/3rms/m4vs", "3,10,16,42,60" ); | |
/*15*/ test( "336a/3rkw/jlor/3akw", "6,21,24,85" ); | |
/*16*/ test( "21om/87bv/67cv", "9,15,18,27,30,45" ); | |
/*17*/ test( "4hhx/056u/4rvu", "6,20,33,39,42,110" ); | |
/*18*/ test( "b0sh/pgxt/88lq/amux", "3,20,35,44,90" ); | |
/*19*/ test( "c0hc/h6md/fdmk/4cfj", "2,35,49,60,77" ); | |
/*20*/ test( "0liz/ilvz/0lvr/0rvz", "78,104,108,144" ); | |
/*21*/ test( "81ib/q1zb/8cir/qczr", "90,100,135,150" ); | |
/*22*/ test( "h7t8/t8ye/g8he/hetz", "6,12,30,72,252" ); | |
/*23*/ test( "b5qy/o6qc/21tb/qoyu/b5eu", "2,10,18,48,57" ); | |
/*24*/ test( "eajn/jkln/j8ua/nkun/u4wy", "6,21,22,60,65" ); | |
/*25*/ test( "wwzz/nfuw/nfzz/41vw/l1r2/nfrg", "4,6,9,17" ); | |
/*26*/ test( "46rb/t6xb/m7zk/4hrj/thxj", "4,8,10,16,20,36" ); | |
/*27*/ test( "olwx/ogul/ogwx/ogux/agux", "10,24,30,72,238" ); | |
/*28*/ test( "b7un/c3hv/fiyo/h6xm", "2,10,12,13,16,20,52,143" ); | |
/*29*/ test( "d6qa/o4qr/tcur/9bto", "2,4,6,8,15,26,39,44,195" ); | |
/*30*/ test( "2lsx/54hf/k3yi/8dhw/bhny", "3,12,18,24,33,60,66" ); | |
/*31*/ test( "apcx/a8pv/7uwx/a2c8/c8pu", "2,4,9,10,12,13,34,286" ); | |
/*32*/ test( "7yjz/jywz/7ejz/j5wy/bejz/jewy", "4,8,13,80,117,160,260" ); | |
/*33*/ test( "d0wk/5dqu/6lqs/77ae/f4mq/56bm", "3,4,5,7,14,18,28,35,49,63" ); | |
/*34*/ test( "d4gn/94in/d4rs/94xu/97xn", "6,9,12,18,27,32,48,64,70,96,144" ); | |
/*35*/ test( "l5wh/wdxn/60xs/c5fd/jpwx/mgqx", "4,9,10,12,15,18,20,24,30,32" ); | |
/*36*/ test( "5178/58xk/uixk/71u8/71uk/71ui/51ui", "4,6,14,20,30,46,161,230" ); | |
/*37*/ test( "m8sp/mosp/2imp/i8sp/2isp/i8si/misp/iosp", "4,6,24,36,40,60,112" ); | |
/*38*/ test( "34d5/253k/f4m5/m5rk/2o3u/3udy/fumy/moru", "6,7,10,15,28,30,40,75" ); | |
/*39*/ test( "2ilw/mbnc/n9wj/9dmy/6qwy/2ekh/9dkh", "1,6,11,18,21,33,72,80,90,96" ); | |
/*40*/ test( "j0le/10uo/q6ue/jeqt/jelf/l6xf", "2,4,5,27,28,32,35,36,40,54,63,432" ); | |
/*41*/ test( "j4mu/31r5/qeyf/0f5h/r0v5/00qi/j5kf/jlru", "3,4,8,9,10,10,20,27,45,52" ); | |
/*42*/ test( "g8kc/dbuv/gbkc/dbgv/evuw/dbui/d8kw", "1,4,6,9,10,12,21,24,39,52,70,130" ); | |
/*43*/ test( "apry/a0ry/a0hx/60hp/6xhy/a0hp/a0hy/6phy/6phx", "4,7,32,56,90,100,175,250" ); | |
/*44*/ test( "1eok/33by/d0kz/1rnw", "10,10,12,12,14,15,16,21,24,35,40,42,48,49,56,88,98" ); | |
/*45*/ test( "0qbs/6cws/l6xj/659q/03lc/bclp/96dj/96wc", "10,12,13,14,14,21,24,42,48,66,77" ); | |
/*46*/ test( "q8sr/98yu/clyn/s8yl/9lqr/0rsu/0l9m/0n9u", "4,8,9,12,26,27,28,36,42,57,78,221" ); | |
/*47*/ test( "5sjy/jbsy/8dgp/gkvp/gdvh/jhvp/i2vk", "3,4,6,8,9,12,15,15,18,27,36,45,81,84,96" ); | |
/*48*/ test( "42va/10nf/23l6/c2uw/3hpo/4ofu/m7sv", "3,5,6,8,8,15,18,20,21,24,27,32,48,50,63,70" ); | |
/*49*/ test( "84lj/10j1/wcxd/ljnl/1njx/01xd/00x1/81wq/1c8q", "1,1,4,7,11,14,18,21,33,70,78,117,126" ); | |
/*50*/ test( "kfvg/76vq/136d/6gvq/6g7q/137g/7dmz/63m6/m3vz", "2,3,9,10,27,45,50,81,81,90,105,135,150" ); | |
/*51*/ test( "4eht/38jt/jeym/htjv/eeyv/eejt/3myv/h1jt/hejm", "4,6,7,12,14,14,16,21,22,24,70,80,120,135" ); | |
/*52*/ test( "smuz/04c7/28zc/83ri/cihu/8flm/masw/8ivo", "2,4,6,8,10,10,12,16,16,20,22,24,24,30,30,36,39,48" ); | |
/*53*/ test( "7fuu/17fd/6cpg/fghu/ahnt/adww/rhxz/4hxl/0pby", "1,2,2,2,3,3,4,4,4,5,8,8,9,10,12,12,12,12,15,15,16,16,20,24,27,30,32,48" ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment