class Sudoku
attr_accessor :run
class Grid # 포인터like 사용을 위한 클래스
attr_accessor :v
end
def initialize
@r, @c, @s=3.times.map { 9.times.map { [[]]*9 } } # 행,열,구역
9.times { |i| 9.times { |j| @c[j][i] = @r[i][j] = Grid.new } }
(0..8).step(3) { |rn| (0..8).step(3) { |cn|
3.times { |i| 3.times { |j|
@s[rn + (cn / 3)][i * 3 + j] = @r[rn + i][cn + j]
} }
} }
end
def find_abl_num rn, cn # 해당 칸의 가능한 숫자 세트 탐색
nums, sn = Array.new(9, true), rn / 3 * 3 + cn / 3 # true로 초기화,섹터 번호 구함
9.times { |i| # 행(*r),열(*c),섹터(*s) 탐색
nums[@r[rn][i].v - 1] = false if @r[rn][i].v > 0 # 채워져 있으면 마킹
nums[@c[cn][i].v - 1] = false if @c[cn][i].v > 0
nums[@s[sn][i].v - 1] = false if @s[sn][i].v > 0
}
[rn, cn, nums]
end
def find_min_possible #가장 경우의 수가 적은칸의 세트 리턴
size, minset = 10, nil
9.times { |i| 9.times { |j|
next if @r[i][j].v>0 # 채워진칸 무시
temp = find_abl_num(i, j)
nsize= temp[2].count(true)
return [10, 10, 0] if nsize==0 # 빈칸인데 가능한 숫자가 없으면 오답 표시 리턴
return temp if nsize==0 # 가능한 숫자가 하나일 경우 바로 리턴
next unless nsize<size
size, minset = nsize, temp
} }
minset
end
def solve
rn, cn, nums = find_min_possible # 가장 경우의 수가 적은 칸 탐색
return true if nums==nil # 더 이상 빈칸이 없음을 뜻함. 풀린것!
return false if rn>9 # 불가능한 칸이 있음을 뜻함
(1..9).each { |i|
next unless nums[i-1] # 불가능한 숫자는 스킵
@r[rn][cn].v = i # 가능한 숫자를 채움
return true if solve # 채운걸 풀어봄 그게 풀렸으면 탈출
}
# 채울 수 있는 숫자가 없거나 다 채워봤는데 안된거니까
@r[rn][cn].v = 0 # 지우고
false # 오답 리턴
end
def run
9.times { |i| gets.strip.chars.map(&:to_i).each_with_index { |n, j|
@r[i][j].v=n
} }
puts solve ? '[Solved]' : '[No solution]'
@r.each { |row| puts row.map(&:v).join }
end
end
Sudoku.new.run
Y2xhc3MgU3Vkb2t1CglhdHRyX2FjY2Vzc29yIDpydW4KCWNsYXNzIEdyaWQgIyDtj6zsnbjthLBsaWtlIOyCrOyaqeydhCDsnITtlZwg7YG0656Y7IqkCgkJYXR0cl9hY2Nlc3NvciA6dgoJZW5kCgoJZGVmIGluaXRpYWxpemUKCQlAciwgQGMsIEBzPTMudGltZXMubWFwIHsgOS50aW1lcy5tYXAgeyBbW11dKjkgfSB9ICMg7ZaJLOyXtCzqtazsl60KCQk5LnRpbWVzIHsgfGl8IDkudGltZXMgeyB8anwgQGNbal1baV0gPSBAcltpXVtqXSA9IEdyaWQubmV3IH0gfQoJCSgwLi44KS5zdGVwKDMpIHsgfHJufCAoMC4uOCkuc3RlcCgzKSB7IHxjbnwKCQkJMy50aW1lcyB7IHxpfCAzLnRpbWVzIHsgfGp8CgkJCQlAc1tybiArIChjbiAvIDMpXVtpICogMyArIGpdID0gQHJbcm4gKyBpXVtjbiArIGpdCgkJCX0gfQoJCX0gfQoJZW5kCgoJZGVmIGZpbmRfYWJsX251bSBybiwgY24gIyDtlbTri7kg7Lm47J2YIOqwgOuKpe2VnCDsiKvsnpAg7IS47Yq4IO2DkOyDiQoJCW51bXMsIHNuID0gQXJyYXkubmV3KDksIHRydWUpLCBybiAvIDMgKiAzICsgY24gLyAzICMgdHJ1ZeuhnCDstIjquLDtmZQs7IS57YSwIOuyiO2YuCDqtaztlagKCQk5LnRpbWVzIHsgfGl8ICMg7ZaJKCpyKSzsl7QoKmMpLOyEue2EsCgqcykg7YOQ7IOJCgkJCW51bXNbQHJbcm5dW2ldLnYgLSAxXSA9IGZhbHNlIGlmIEByW3JuXVtpXS52ID4gMCAjIOyxhOybjOyguCDsnojsnLzrqbQg66eI7YK5CgkJCW51bXNbQGNbY25dW2ldLnYgLSAxXSA9IGZhbHNlIGlmIEBjW2NuXVtpXS52ID4gMAoJCQludW1zW0BzW3NuXVtpXS52IC0gMV0gPSBmYWxzZSBpZiBAc1tzbl1baV0udiA+IDAKCQl9CgkJW3JuLCBjbiwgbnVtc10KCWVuZAoKCWRlZiBmaW5kX21pbl9wb3NzaWJsZSAj6rCA7J6lIOqyveyasOydmCDsiJjqsIAg7KCB7J2A7Lm47J2YIOyEuO2KuCDrpqzthLQKCQlzaXplLCBtaW5zZXQgPSAxMCwgbmlsCgkJOS50aW1lcyB7IHxpfCA5LnRpbWVzIHsgfGp8CgkJCW5leHQgaWYgQHJbaV1bal0udj4wICMg7LGE7JuM7KeE7Lm4IOustOyLnAoJCQl0ZW1wID0gZmluZF9hYmxfbnVtKGksIGopCgkJCW5zaXplPSB0ZW1wWzJdLmNvdW50KHRydWUpCgkJCXJldHVybiBbMTAsIDEwLCAwXSBpZiBuc2l6ZT09MCAjIOu5iOy5uOyduOuNsCDqsIDriqXtlZwg7Iir7J6Q6rCAIOyXhuycvOuptCDsmKTri7Ug7ZGc7IucIOumrO2EtAoJCQlyZXR1cm4gdGVtcCBpZiBuc2l6ZT09MCAjIOqwgOuKpe2VnCDsiKvsnpDqsIAg7ZWY64KY7J28IOqyveyasCDrsJTroZwg66as7YS0CgkJCW5leHQgdW5sZXNzIG5zaXplPHNpemUKCQkJc2l6ZSwgbWluc2V0ID0gbnNpemUsIHRlbXAKCQl9IH0KCQltaW5zZXQKCWVuZAoKCWRlZiBzb2x2ZQoJCXJuLCBjbiwgbnVtcyA9IGZpbmRfbWluX3Bvc3NpYmxlICMg6rCA7J6lIOqyveyasOydmCDsiJjqsIAg7KCB7J2AIOy5uCDtg5Dsg4kKCQlyZXR1cm4gdHJ1ZSBpZiBudW1zPT1uaWwgIyDrjZQg7J207IOBIOu5iOy5uOydtCDsl4bsnYzsnYQg65y77ZWoLiDtkoDrprDqsoMhCgkJcmV0dXJuIGZhbHNlIGlmIHJuPjkgIyDrtojqsIDriqXtlZwg7Lm47J20IOyeiOydjOydhCDrnLvtlagKCQkoMS4uOSkuZWFjaCB7IHxpfAoJCQluZXh0IHVubGVzcyBudW1zW2ktMV0gIyDrtojqsIDriqXtlZwg7Iir7J6Q64qUIOyKpO2CtQoJCQlAcltybl1bY25dLnYgPSBpICMg6rCA64ql7ZWcIOyIq+yekOulvCDssYTsm4AKCQkJcmV0dXJuIHRydWUgaWYgc29sdmUgIyDssYTsmrTqsbgg7ZKA7Ja067SEIOq3uOqyjCDtkoDroLjsnLzrqbQg7YOI7LacCgkJfQoJCSMg7LGE7Jq4IOyImCDsnojripQg7Iir7J6Q6rCAIOyXhuqxsOuCmCDri6Qg7LGE7JuM67Sk64qU642wIOyViOuQnOqxsOuLiOq5jAoJCUByW3JuXVtjbl0udiA9IDAgIyDsp4DsmrDqs6AKCQlmYWxzZSAjIOyYpOuLtSDrpqzthLQKCWVuZAoKCWRlZiBydW4KCQk5LnRpbWVzIHsgfGl8IGdldHMuc3RyaXAuY2hhcnMubWFwKCY6dG9faSkuZWFjaF93aXRoX2luZGV4IHsgfG4sIGp8CgkJCUByW2ldW2pdLnY9bgoJCX0gfQoJCXB1dHMgc29sdmUgPyAnW1NvbHZlZF0nIDogJ1tObyBzb2x1dGlvbl0nCgkJQHIuZWFjaCB7IHxyb3d8IHB1dHMgcm93Lm1hcCgmOnYpLmpvaW4gfQoJZW5kCmVuZAoKU3Vkb2t1Lm5ldy5ydW4=