私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「カードを使って数を作ろう(簡単編)」(CodeIQ)を参照してください。
【概要】
カードが何枚かあります。
カードには、一桁の数字が書いてあります。
カードをn枚選んで並べて数を作ります。
たとえば、「3」「1」「4」「1」「5」が書いてあるカードから 3枚(つまり、n=3)を選んで並べると「341」や「115」を作ることができます( 「334」や「31」は作れません)。
作れる数のうち、ある数 m にもっとも近い数を答えてください。
【入出力】
入力は
4,1234,1/2/2/3/9/9/9
のような感じです。
コンマ区切りで、n(カードの枚数)、m(この値に近い数を作る)、カード列 が並んでいます。
カード列は、カードに書いてある数字をスラッシュ区切りで並べたものです。
出力は、
1232
のような感じです。最も近い数が2つある場合は、小さい順に両方共出力してください。
1233,1235
のような感じです。
末尾の改行はあってもなくても構いません。
作れる数がひとつもない場合には、-を出力してください。
【補足】
「01」のような、「0」で始まる二桁以上の数は作れません。一桁の「0」はOKです。
カードの枚数は8枚以下です。
n は 1以上で、カードの枚数を超えることはありません。
m は 0以上、10の9乗以下です。
6 のカードを逆さにして 9 として使ったりすることはできません。
不正な入力に対処する必要はありません。
Rubyで解答しています。
#!/usr/bin/ruby def toNum(ptn) if (ptn.size > 1) && (ptn.last == 0) then return -1 end ret = 0 ptn.each_with_index{|n, i| ret += n * (10**i) } return ret end def solve(n,m,cards) ret = [] bs = 9999999999 cards.permutation(n){|ptn| v = toNum(ptn) if v<0 then next end s = (m-v).abs if s < bs then ret = [v] bs = s elsif s == bs then ret << v end } return ret.uniq.sort end def printResult(ptn) if ptn.empty? then puts("-") else puts(ptn.join(",")) end end while line = gets line.strip! if line.empty? then next end sc = line.split(",") n = sc[0].to_i m = sc[1].to_i cards = sc[2].split("/").map{|i| i.to_i} ret = solve(n,m,cards) printResult(ret) end
問題に簡単と書かれている通りそれほど難しくありません。
計算量も8!しかないので総当たりで大丈夫です。単純に全パターン列挙するので入力値mに近いかどうかはmと列挙した値の差の絶対値でチェックすれば大丈夫です。
引数n,m,cardsは入力値をパースして数値にしたものです。
cardsは[1,2,2,3,9,9,9]のようなカードの値の配列です。
Rubyなのでcardsにpermutation()を使ってパターンを列挙します。
そしてtoNum()でパターンを数値に変換します。toNum()はエラー値として-1を返すので-1が帰った時は無視します。
そして帰ってきた値(v)とmの差の絶対値を取り、以前の差の絶対値より小さくなっていたら結果を更新します。以前の差の絶対値と同値なら結果に追加します(20〜26行目)。
結果は同じ値が重複していたり、順番が昇順になっていないのでuniqしてsortしてから返します。
パターンは配列でくるので1つずつを各桁の値として数値に変換します(7〜9行目)。なお、プログラムでは配列の最初の桁が1の位、2つ目が10の位、…、となります。
ただし、1桁の値でない限り戦闘に0はおけないという仕様なので4行目でチェックして先頭(配列の最後の要素)が0ならエラーリターンします。
これを書いている時に気付きましたが、パターンを数値に直すのは配列をcardsを数値に変換せず文字列のまま扱って、joinしてto_iすれば済んでいました。Rubyを始めてから結構Rubyでプログラムを書いたと思いますが、頭の中で考えるコードはC/C++とJavaに近い言語のままのようです。