私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「同じ数字でサンドイッチ」(CodeIQ)を参照してください。
1~Nまでの数字が掛かれたカードがそれぞれ2枚ずつと、ジョーカー1枚があります。
このカードを一列に並べるとき、書かれている数字の数だけ他のカードを挟んで並べることにします。
(ジョーカーは挟まれるだけで、挟む必要はありません。)
例えば、1, 2, 3のカードがそれぞれ2枚あるとき、 ジョーカーを「0」で表すと
2, 3, 1, 2, 1, 3, 0
2, 3, 0, 2, 1, 3, 1
…
などのように並べられます。
※いずれも1で挟まれたカードは1枚、2で挟まれたカードは2枚、3で挟まれたカードは3枚です。
N=5のときは、以下のような例で挟めます。
1, 5, 1, 2, 3, 4, 2, 5, 3, 0, 4
1, 4, 1, 5, 3, 0, 4, 2, 3, 5, 2
…
標準入力から N が与えられるとき、上記のように挟んだ並べ方の中から、ジョーカーが最も右側に来る並べ方を求め、その位置を標準出力に出力してください。
例えば、N=3, N=5 のときは上図のそれぞれ一行目が該当しますので、以下のように出力します。
Nは9以下の正の整数とし、上記のような挟み方が存在しない場合は0を出力してください。
【入出力サンプル】
標準入力
3
標準出力
7
Rubyで解答しています。
#!/usr/bin/ruby $POS = 0 def getPos(pos) i = 0 while (pos & (1<<i)) != 0 i += 1 end return i+1 end def solve(n, m, now) # printf("n=%d m=%d now=%s\n", n, m, now.to_s(2)) if m == 0 then x = getPos(now) $POS = x if x > $POS return end pos = (1 << (m+1)) | 1 for i in 0...(n*2-m) nxt = pos << i next if (now & nxt) != 0 solve(n, m-1, now | nxt) end end # main while line = gets line.strip! next if line.empty? solve(line.to_i, line.to_i, 0) p $POS end
$POSは最も右側にジューカーが置かれたときの位置の最大値を保持する変数です。
入力値を数値にしてsolve()に渡し、結果を得ます。
引数nは入力値です。
引数mは配置するカードの番号です。最初の呼び出し時はnと同値になります。
nowはカードがすでに置かれている場所を1、置かれていない場所を0とした整数です。配置が決定したカードの番号は関係ない(その場所にカードがあるかどうかだけが重要)なので処理の高速化のためこの様にしました。なお、問題とは左右反転して扱います。
15〜19行目はジューカーが置かれる場合(m=0の場合)で、getPos()でその場所を求めます。このときnowの中で最初に0になるビットをジョーカーの位置とする(他の場所は1で埋まっているので1の連続の中から0を探す)ため、ジョーカーの場所のビットを立てる処理はしません。
位置が求まり、$POSより大きかったら$POSを更新します。
21行目の処理で数字mのカードを配置する場所を決めます。
例えば、m=3ならpos=0b10001になるので問題の通りに配置できます。
22〜26行目のループで配置可能な場所を全て試します。
1枚目のカードが(n*2-m)番目になったら配置可能な領域からはみ出すのでその前までが配置可能な場所になります。
posをiビット左シフトしてnowと論理積をとった気に0でない場合、同じ場所にはすでにカードが置かれていることになります。なので無視します。
posとnowの論理積が0ならsolve(n, m-1, now | nxt)を再帰呼び出しします。
ジョーカーの位置を探します。
ジョーカーの位置は0b11011111の様になっている0の場所なのでビットシフトしながら位置を探します。
答えは0始まりでなく1始まりなので結果を+1して返します。
最初に書いた様に一度カードの間にその数字枚数のカードを配置する問題をやったことがあったので配置を作るのは簡単に考えつきました。
ただ、総当たりしか方法を思いつかず(うまくメモ化する方法を考えつかなかった)、とりあえず時間計測してみて考えようと思ったら、0.2秒あまりで終わったのでそのまま解答としました。