正の整数を選択的に殺す


21

前書き

算術Gaolは、正の整数を無効にする特別な機能です。ただし、最近、正の整数がエスケープしようとしています。したがって、監視員は、他の整数にメッセージを送信するために、いくつかの正の整数を削除することを決定しました。彼らは、ソフトウェアエンジニアを雇って、最大の効果を得るためにどの整数を排除するかを決定するプログラムを作成しました。

入力の説明

入力は、STDIN、コマンドライン引数、またはユーザー入力関数(などraw_input)を介して与えられます。関数の引数や事前初期化された変数として使用することはできません(たとえば、このプログラムは変数への入力を想定していますx)。

入力の最初の行には、単一の正の整数nが含まれます8 >= n >= 3。その後に、セットの文字nを含む行が続きます。入力例を次に示します。n[1,2,3,4,5,6,7,8,9]

5
22332
46351
65455
24463
65652

出力の説明

監視員は、次の条件が満たされるように数字を削除したいと考えています。

  • 結果のグリッドの各行と列には、数字が2回表示されません。
  • 排除された2つの数字が水平または垂直に隣接することはありません。
  • 生き残った番号は、直交する連続したグループを形成する必要があります-生き残った番号から、水平方向と垂直方向にのみ移動し、排除された番号を決して越えない他の生き残った番号に移動できます。

入力を出力し(最初の行を差し引いた)、除去された数値をに置き換え#ます。

複数のソリューションが存在する場合があります。その場合、任意のソリューションを出力できます。

解決策がない場合もあります。その場合、文字列を出力しますno answer

入力例の可能な出力は次のとおりです。

#2#3#
46351
6#4#5
24#63
#56#2

入力と出力の例

各入力には複数の出力があるため、これらの出力は単なる例です。

入力:

5
46551
51565
32654
14423
43244

出力:

46#51
#156#
326#4
1#423
#324#

入力:

7
7183625
1681563
5238564
8786268
1545382
3814756
5325345

出力:

71#362#
#6815#3
5238#64
#7#62#8
154#382
3814756
#325#4#

入力:

8
21534768
75196287
68392184
96244853
44865912
76516647
89751326
43698979

出力:

21#34768
#5196287
683#21#4
9#24#853
#4865912
7#51#64#
89751326
436#8#7#

入力:

4
2222
2331
3112
1322

出力:

no answer

4
シングルとも呼ばれます。)
ドアノブ

このパズルは素晴らしいです。ありがとうございました。ソリューションに取り組んでいる
チャールズが

1
私はこのパズルが好きですが、ブラウザでJavaScriptを使用して「そのまま」答えることはできません。これは、唯一のユーザー入力メソッドでpromptは複数行の入力が許可されていないためです。
edc65

回答:


0

ルビー、346 344 329 316バイト、sl∞∞∞∞∞∞∞w

これはコード高速でなく、コードゴルフです。

N=/!/=~G=$*[1..-1]*?!
M=N*N
g=->i{(!G[i]||G[i]<?*||i<0||A[i])&&break
A[i]=0
[1,N+1,-1,-1-N].map{|a|g[i+a]}}
f=->w,a{A=[];g[/\d/=~G=a];/#.{#{N}}?#/!~G&&/(\d)([^!]*|.{#{N}}.{#{O=N+1}}*)\1/!~G&&A.count(0)+w==M&&N.times.map{|m|puts G[m*(1+N),N]}&&exit
(w...M).map{|j|b=a+'';b[j]=?#
w<M&&f[w+1,b]}}
f[0,G]
$><<"no answer"

次のように使用します。

mad_gaksha@madlab ~/Applications/Tools $ ruby -W0 c50442.rb 3 323 312 231
#23
312
231

フラグ-W0は必要ありませんが、警告を無効にするかリダイレクトするために追加することをお勧めしますstderr...

n = 6,7,8の例で実行するのに十分な忍耐力があるかどうか教えてください

変更履歴

  • eachmap、@ NotThatCharlesに感謝
  • regexp@NotThatCharlesが提案したものと同様に、隣接する削除と2桁の同じ数字をチェックします
  • 最適化された読み取り入力
  • 小さくて遅い

サイズの漸進的な改善:最後から2番目の行\dよりも短い[^#]M.times.to_aよりも長い(0..M-1).to_a
チャールズが

@NotThatCharlesヒントをありがとう!ただし、;)よりM.times.to_a1バイト短くなり(0..M-1).to_aます(0...M).to_aが、同じ長さです。
ブルトレンジ

...カウントは難しい
チャールズが

n = 8のときに実際に完了しますか?
チャールズではない

@NotthatCharles十分に待たされるか、超高速のPCを使用する場合、そうです。これは速度に関する制限のないコードゴルフなので、コード長を優先しました
...-blutorange

5

ルビー-541 ...、 394

基本的なアルゴリズムは、重複を積極的に選択して再帰的に深さ優先で検索し、1行目、1列目、2行目などを調べて、2つの隣人が殺されないことと、グリッドが接続されていることを確認します(つまりbreak if、そこに、そしてその前に来るビット)。

K=(0...(N=gets.to_i)*N).to_a
J=gets(p).split*''
H=->m{K&[m+1,m-1,m+N,m-N]}
Q=->k{s=[k[j=0]]
(j=s.size
s.map{|x|(s+=H[x]&k).uniq!})while s[j]
break if(K-k).any?{|m|(H[m]-k)[0]}||k!=k&s
$><<K.map{|m|[k.index(m)?J[m]:?#,m%N>N-2?"
":p]}*''|exit if !g=((0...N*2).map{|x|(k.select{|m|m.divmod(N)[x/N]==x%N}.group_by{|m|J[m]}.find{|l,c|c[1]}||[])[1]}-[p]).min
g.map{|d|Q[k-g<<d]}}
Q[K]
puts"no answer"

いくつかの巧妙なトリック:

if w[1]は、よりもはるかに短く、if !w.one?少なくとも1人のメンバーがいることがわかっている場合は、同じ結果になります。

同様に、ブロックがない場合[0]よりも短くなり、(技術的にはに似た)のかわいいショートカットになりますany?s[j]j<s.sizej.abs<s.size

そしてy%N+(y/N).i、はるかに短いですComplex(y%N,y/N)

また、文字列を生成するための2つの複雑な条件がある[cond1?str1a:str1b,cond2?str2a:str2b]*''場合、すべての括弧または#{}s を追加するよりも行う方が短い場合があります。

アンゴルフと説明:

(これは531バイトバージョンからです。変更を加えました。最も注目すべきは、製品への呼び出しを削除したことです。一度に行/列ごとに1桁を解決し、Jは現在、整数。すべての座標は整数です。)

H 隣人を計算する

def H m 
  # m, like all indices, is a complex number 
  #    where the real part is x and the imaginary is y
  # so neighbors are just +/-i and +/-1

  i='i'.to_c
  neighborhood = [m+1, m-1, m+i, m-i]

  # and let's just make sure to eliminate out-of-bounds cells
  K & neighborhood
end

N グリッドのサイズです

N = gets.to_i

K マップのキー(複素数)

# pretty self-explanatory
# a range of, e.g., if N=3, (0..8)
# mapped to (0+0i),(1+0i),(2+0i),(0+1i),(1+1i),(2+1i),...
K = (0..N**2-1).map{|y| (y%N) +(y/N).i }

J 入力マップ(刑務所)

# so J is [[0+0,"2"],[0+1i,"3"],....].to_h
J=K.zip($<.flat_map {|s|
  # take each input line, and...
  # remove the "\n" and then turn it into an array of chars 
  s.chomp.chars
}).to_h

k 殺されていないキーです

# starts as K

Q 主な再帰的方法です

def Q k
  j=0 # j is the size of mass
  # the connected mass starts arbitrarily wherever k starts
  mass=[k[0]]
  while j < s.size # while s hasn't grown
    j = mass.size   
    mass.each{|cell|
      # add all neighbors that are in k
      (mass+=H[cell] & k).uniq!
    }
  end
  # if mass != k, it's not all orthogonally connected
  is_all_connected = k!=k&mass

  # (K-k) are the killed cells 
  two_neighbors_killed = (K-k).any?{|m|
    # if any neighbors of killed cells aren't in k,
    # it means it was killed, too 
    (H[m]-k)[0]
  }
  # fail fast
  return if two_neighbors_killed || is_all_connected

  def u x
    x.group_by{|m|J[m]}.select{|l,c|c[1]}
  end

  rows_with_dupes = Array.new(N){|r|u[k.select{|m|m.imag==r}]}

  cols_with_dupes = Array.new(N){|r|u[k.select{|m|m.real==r}]}

  # dupes is an array of hashes
  # each hash represents one row or column.  E.g.,
  #   {
  #     "3"=>[(0+0i),(1+0i),(3+0i)],
  #     "2"=>[(2+0i),(4+0i)]
  #   }
  # means that the 0th, 1st and 3rd cells in row 0
  # all are "3", and 2nd and 4th are "2".
  # Any digits without a duplicate are rejected.
  # Any row/col without any dupes is removed here.
  dupes = (rows_with_dupes+cols_with_dupes-[{}])

  # we solve one row at a time
  first_row = dupes[0]

  if !first_row
    # no dupes => success!
    J.map{|m,v|k.member?(m)?v:?#}.each_slice(N){|s|puts s*''}
    exit
  else
    # the digit doesn't really matter
    t=first_row.values

    # cross-multiply all arrays in the row to get a
    # small search space. We choose one cell from each
    # digit grouping and drop the rest.
    t.inject(:product).map{ |*e|
      # Technically, we drop all cells, and add back the
      # chosen cells, but it's all the same.
      new_k = k-t.flatten+e.flatten

      # and then search that space, recursively
      Q[new_k]
    }
  end
end

コードは次のコマンドで実行されます。

# run with whole board
Q[K]

# if we get here, we didn't hit an exit, so we fail
puts"no answer"

変更履歴

394は以下に@blutorangeの提案を追加し、さらに多くの操作を切り詰めました。

408は出力をもう一度修正しました。とにかく1行だけを取っているので、.min代わりに使用し.inject(:+)てください。

417出力計算の短縮

421は複素数を削除しました。整数を使用してください。バンドルを保存する

450以上の入力の改善

456入力の改善

462段階的な改善-特に find、ではないselect

475u列/列のデュープビルダーをドロップして押しつぶしました

503は、一度に行/列ごとに1つの重複数字のみを解決します。

530のmap &:pop代わりに使用values

531デュープ配列を作成するラムダを引き出します

552おっと!要件を逃した

536のデュープアレイの人口がわずかに改善されました(以前のバージョンd

541イニシャル


ラムダの内部でbreakreturn、の代わりに使用でき、もう1バイト節約できます。私は本当にこれが好きです、たくさんのトリックとはるかに速い。
ブルトレンジ

@blutorangeありがとう!適用されます。ただし、344をヒットする方法はまだあります。
チャールズが

少し長くなりますが、そうでなければ、うまくできているように見えます。もう1つ見たことがあります。2行目では、変数aは1回しか使用されないため、それを作成できますJ=gets(p).split*''。cli引数を試しましたか、私の答えをご覧ください。
ブルトレンジ

3

HTML + JavaScript(ES6)459

HTMLテキストエリアを使用して複数行の入力を取得します。

テストするには、Firefoxでスニペットを実行します。必要に応じてtextareaを拡大し、textarea内に入力を貼り付け(正確な入力、行に余分なスペースがないように注意)、タブで移動します。結果が追加されます。

方法:再帰的な深さ優先の首長。すべてのテストケースに対して非最適なソリューションを数秒で見つけます(ゴーデゴルフですが、一般的なテストケースでは有効な回答終了する必要があります)

<textarea onchange="s=this.value,
  z=s[0],o=-~z,k=[],X='no answer',f='#',
  (R=(s,t,h={},r=[],d=0)=>(
    s.map((v,i)=>v>0&&[i%o,~(i/o)].map(p=>h[p+=v]=[...h[p]||[],i])),
    [h[i][1]&&h[i].map(p=>r[d=p]=p)for(i in h)],
    d?r.some(p=>(
      (s[p+o]!=f&s[p-o]!=f&s[p-1]!=f&s[p+1]!=f)&&
      (
        g=[...s],g[p]=f,e=[...g],n=0,
        (F=p=>e[p]>0&&[1,-1,o,-o].map(d=>F(p+d),e[p]=--n))(p<o?p+o:p-o),
        t+n==0&&!k[g]&&(k[g]=1,R(g,t-1))
      )
    )):X=s.join('')
  ))([...s.slice(2)],z*z-1),this.value+=`

`+X"></textarea>

初挑戦

方法:ユーザースタックを使用する、深さ優先のSerarch、非再帰的。
関数はBreadth First Searchで簡単に変換でき、に変更さl.popれるl.shiftため、スタックの代わりにキューを使用しますが、遅すぎて、とにかく最適なソリューションを見つけることができるかどうかわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.