質問投稿サイトの問題を解く

また rscの日記さんのところにあった問題です。おもしろそうなので Ruby で解いてみました。元の問題はこれです。

問題をコピペしておきます。

円卓の判断推理 問題


A~E の5人が円卓に等間隔で着席している。5人のうち女性は2人で、この2人の席は隣り合っていない。
5人はそれぞれ赤,青,白,黒,緑のシャツを着ており,同じ色のシャツを着ている者はいない。A~D の4人が以下のように述べている。


A「私の左隣には黒いシャツを着た人が座っており,その左隣は男性である」
B「私の右隣には白いシャツを着た人が座っており,その右隣は女性である。」
C「私の左隣には緑のシャツを着た人が座っており,その左隣は男性である」
D「私の隣には青いシャツを着た人が座っている。私ともう1人の両隣に座ってい
る人のシャツは黒ではない。」


この場合で考えられる座席パターンを教えてください。


円順列なので A を固定しています。あとはシンプルなので特に問題はないと思います。変数 $po, sex, col にはそれぞれ位置、性(:m が男性、:f が女性)、色がハッシュで入っています。

class Symbol
  def left;  $po.key(($po[self] - 1) % 5); end
  def right; $po.key(($po[self] + 1) % 5); end
end

def neighbor(sex)
  ar = []
  sex.each_key {|k| ar << $po[k] if sex[k] == :f}
  ar.sort!
  (ar[0] + 1 == ar[1]) or (ar[0] == 0 and ar[1] == 4)
end

ans = []
[1, 2, 3, 4].permutation(4) do |ps|
  $po = {:A=>0}
  $po[:B] = ps[0]; $po[:C] = ps[1]; $po[:D] = ps[2]; $po[:E] = ps[3]
  [:A, :B, :C, :D, :E].combination(2) do |s|
    sex = {:A=>:m, :B=>:m, :C=>:m, :D=>:m, :E=>:m}
    sex[s[0]] = :f; sex[s[1]] = :f
    next if neighbor(sex)
    [:A, :B, :C, :D, :E].permutation(5) do |c|
      col = {}
      col[c[0]] = ""; col[c[1]] = ""; col[c[2]] = ""
      col[c[3]] = ""; col[c[4]] = "" 
      next if col[:D] == ""
      if (col[:A.left] == "" and sex[:A.left.left] == :m) and
	 (col[:B.right] == "" and sex[:B.right.right] == :f) and
         (col[:C.left] == "" and sex[:C.left.left] == :m) and
         ( (col[:D.left] == "" and col[:D.right] != "") or
	    (col[:D.right] == "" and col[:D.left] != "") ) then
	ans << [$po, sex, col]
      end
    end
  end
end
p ans

答えは

[[{:A=>0, :B=>2, :C=>3, :D=>1, :E=>4}, {:A=>:m, :B=>:f, :C=>:m, :D=>:m, :E=>:f}, {:D=>"", :A=>"", :C=>"", :E=>"", :B=>""}],
[{:A=>0, :B=>4, :C=>2, :D=>1, :E=>3}, {:A=>:m, :B=>:f, :C=>:m, :D=>:f, :E=>:m}, {:E=>"", :C=>"", :A=>"", :B=>"", :D=>""}]]

と出力されます。左まわりに「(A男青)(D男赤)(B女緑)(C男白)(E女黒)」か「(A男白)(D女緑)(C男青)(E男赤)(B女黒)」と並ぶのが最終的な回答になります。