マトリックスを通る最適なパス


19

正の整数で構成される行列を指定すると、左上の要素から右下に移動するときに最小の合計のパスを出力します。垂直、水平、斜めに移動できます。上/下、右/左、および斜めにすべての側面に移動できることに注意してください。

例:

 1*   9    7    3   10    2    2
10    4*   1*   1*   1*   7    8
 3    6    3    8    9    5*   7
 8   10    2    5    2    1*   4
 5    1    1    3    6    7    9*

最小の合計を与えるパスはアスタリスクでマークされ、次の合計になります:1 + 4 + 1 + 1 + 1 + 5 + 1 + 9 = 23

テストケース:

1   1   1
1   1   1
Output: 3

 7    9    6    6    4
 6    5    9    1    6
10    7   10    4    3
 4    2    2    3    7
 9    2    7    9    4
Output: 28

2  42   6   4   1
3  33   1   1   1
4  21   7  59   1
1   7   6  49   1
1   9   2  39   1
Output: 27 (2+3+4+7+7+1+1+1+1)

 5    6    7    4    4
12   12   25   25   25
 9    4   25    9    5
 7    4   25    1   12
 4    4    4    4    4
Output: 34 (5+12+4+4+4+1+4)

1   1   1   1
9   9   9   1
1   9   9   9
1   9   9   9
1   1   1   1
Output: 15

 2   55    5    3    1    1    4    1
 2   56    1   99   99   99   99    5
 3   57    5    2    2    2   99    1
 3   58    4    2    8    1   99    2
 4   65   66   67   68    3   99    3
 2    5    4    3    3    4   99    5
75   76   77   78   79   80   81    2
 5    4    5    1    1    3    3    2
Output: 67 (2+2+3+3+4+5+4+3+3+3+1+2+2+1+3+1+1+4+5+1+2+3+5+2+2)

これはので、各言語で最も短いコードが優先されます。


非常によく似ていますが、斜めに移動することはできません。
Mego

7
@WheatWizard私は同意しません。この挑戦が斜めの動きを可能にし、すべての位置が到達可能であるという主に表面的な違いは別として、他の挑戦はパスのコストだけでなくパス自体のリターンを必要とします。両方を返すビルトインを使用している場合を除き、コードは交換できません。
ビーカー

@beaker私は実際に違いを見ません。パスの長さを知るには、パスを見つける必要があります。ここでの違いは、私の意見では出力のわずかな違いであり、この課題は、その課題でまだカバーされていない新しいまたは興味深いものを提供しません。
小麦ウィザード

1
@WheatWizardここでの私の解決策は、パスを見つけられません。パスを見つけることはできますが、ノードを独自の先行ノードにすることを回避するために、独立した先行配列とロジックが必要です。最後にパスを回復することは言うまでもありません。
ビーカー

@beaker私の意見では、変更はかなり簡単です。だまされやすい人の問題は、1つのチャレンジのすべての有効なエントリを最小限の労力で移植できるかどうかではなく、一般的なケースです。ここでのほとんどの努力は移植できると思うだけでなく、この挑戦​​が他の何か新しいことや面白いことを提供するとは思わない。
小麦ウィザード

回答:


8

JavaScript、 442の412 408 358バイト

これは私の最初のPPCG提出です。フィードバックをいただければ幸いです。

(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

これは、入力として多次元配列を取ります。

説明

基本的に、すべてのセルを繰り返しループして、既知の最低コストを調整して、各隣接セルに到達します。最終的に、グリッドは、右下に到達するための総コストがそこに到達するための最低コストである状態になります。

デモ

f=(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

//Tests
console.log(f([[1,1,1],[1,1,1]])===3);
console.log(f([[7,9,6,6,4],[6,5,9,1,6],[10,7,10,4,3],[4,2,2,3,7],[9,2,7,9,4]])===28);
console.log(f([[2,42,6,4,1],[3,33,1,1,1],[4,21,7,59,1],[1,7,6,49,1],[1,9,2,39,1]])===27);
console.log(f([[5,6,7,4,4],[12,12,25,25,25],[9,4,25,9,5],[7,4,25,1,12],[4,4,4,4,4]])===34); 
console.log(f([[1,1,1,1],[9,9,9,1],[1,9,9,9],[1,9,9,9],[1,1,1,1]])===15)
console.log(f([[2,55,5,3,1,1,4,1],[2,56,1,99,99,99,99,5],[3,57,5,2,2,2,99,1],[3,58,4,2,8,1,99,2],[4,65,66,67,68,3,99,3],[2,5,4,3,3,4,99,5],[75,76,77,78,79,80,81,2],[5,4,5,1,1,3,3,2]])===67);

編集:@ETHproductionsに感謝しますに何十ものおいしいバイトを削るのを手伝ってくれて。

@Stewie Griffinに、50バイトを無駄にしたヒントをありがとう。


3
PPCGへようこそ!末尾に向かって削除できる余分なスペースがいくつかあり(合計5カウントします)、}数バイトを節約する必要がある直前のセミコロンは必要ありません。また、変数を宣言する必要もありません。varsを削除すると、合計24バイト節約できると思います。
-ETHproductions

2
PPCGへようこそ=)出発点としてあなたが私の挑戦の1つを選んだことをうれしく思います。私の唯一のコメント:説明が大好きです。ただし、オプションであるため、必要な場合を除き、追加する必要はありません。:)
スチューイーグリフィン

m[v][t]変数として保存t=x+X;v=y+Y;k=m[v][t]することも考えられます。もっと短くなります...?
スティーヴィーグリフィン


6

オクターブ+画像処理パッケージ、175 162 157 151 142 139バイト

@Luis Mendoのおかげで14バイト、@ notjaganのおかげで1バイト節約

function P(G)A=inf(z=size(G));A(1)=G(1);for k=G(:)'B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';B(5,:)-=G(:)';A=reshape(min(B),z);end,A(end)

Image Processingパッケージを使用します。なぜですか?それは誰もがグラフの問題を解決する方法ではありませんか?

オンラインでお試しください!

爆発した

function P(G)
   A=inf(z=size(G));         % Initialize distance array to all Inf
   A(1)=G(1);                % Make A(1) = cost of start cell
   for k=G(:)'               % For a really long time...
      B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';
       %  B=padarray(A,[1,1],inf);     % Add border of Inf around distance array
       %  B=im2col(B,[3,3]);           % Turn each 3x3 neighborhood into a column
       %  B=B+G(:)';                   % Add the weights to each row
      B(5,:)-=G(:)';         % Subtract the weights from center of neighborhood
      A=reshape(min(B),z);   % Take minimum columnwise and reshape to original
   end
   A(end)                    % Display cost of getting to last cell

説明

重みの配列が与えられた場合:

7   12    6    2    4
5   13    3   11    1
4    7    2    9    3
4    2   12   13    4
9    2    7    9    4

すべての要素に到達するコストがInfinityになるようにコスト配列を初期化します。ただし、コストがその重みに等しい開始点(左上の要素)を除きます。

  7   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

これは反復0です。後続の反復ごとに、セルに到達するためのコストは次の最小値に設定されます。

  • その要素に到達するための現在のコスト、および
  • 要素の近傍に到達するための現在のコスト+要素の重み

最初の反復の後、要素(2,2)へのパスのコスト(1ベースのインデックス付けを使用)は次のようになります。

minimum([  7   Inf   Inf]   [13  13  13]) = 20
        [Inf   Inf   Inf] + [13   0  13]
        [Inf   Inf   Inf]   [13  13  13]

最初の反復後の完全なコスト配列は次のようになります。

  7    19   Inf   Inf   Inf
 12    20   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

反復後k、各要素は、最初からほとんどのkステップを踏むことでその要素に到達する最低コストになります。たとえば、(3,3)の要素は、22のコストで2ステップ(反復)で到達できます。

  7    19    25   Inf   Inf
 12    20    22   Inf   Inf
 16    19    22   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

しかし、4回目の反復では、20のコストで4ステップのパスが見つかります。

 7   19   25   24   28
12   20   22   32   25
16   19   20   30   34
20   18   30   34   35
27   20   25   40   39

mxnマトリックスを通るパスは(非常に緩やかな上限として)マトリックス内の要素の数より長くなることはできないため、m*n反復後、すべての要素には、最初からその要素に到達するための最短パスのコストが含まれます。


whileとの間のスペースを削除して-1バイト~
-notjagan

@notjaganからに切り替えましwhileforが、まだあなたのチップを使用できました。ありがとう!
ビーカー

5

JavaScript、197バイト

a=>(v=a.map(x=>x.map(_=>1/0)),v[0][0]=a[0][0],q=[...(a+'')].map(_=>v=v.map((l,y)=>l.map((c,x)=>Math.min(c,...[...'012345678'].map(c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)))))),v.pop().pop())

プリティファイ:

a=>(
  // v is a matrix holds minimal distance to the left top
  v=a.map(x=>x.map(_=>1/0)),
  v[0][0]=a[0][0],
  q=[
     // iterate more than width * height times to ensure the answer is correct
    ...(a+'')
  ].map(_=>
    v=v.map((l,y)=>
      l.map((c,x)=>
        // update each cell
        Math.min(c,...[...'012345678'].map(
          c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)
        ))
      )
    )
  ),
  // get result at right bottom
  v.pop().pop()
)

4

Mathematica 279バイト

基本的な考え方により分離行列エントリと任意の2つの頂点間の有向エッジに対応する頂点を持つグラフを作成することであるChessboardDistanceゼロより大きい未満またはなお1に等しく、これはとして知られるようにたまたまキンググラフそれに対応するので、チェス盤上の王の有効な動き。

FindShortestPath次に、最小パスを取得するために使用されます。EdgeWeightではなくVertexWeightで動作EdgeWeightするため、各有向エッジの宛先に対応する行列エントリとして定義する追加のコードがあります。

コード:

(m=Flatten[#];d=Dimensions@#;s=Range[Times@@d];e=Select[Tuples[s,2],0<ChessboardDistance@@(#/.Thread[s->({Ceiling[#/d[[1]]],Mod[#,d[[1]],1]}&/@s)])≤1&];Tr[FindShortestPath[Graph[s,#[[1]]->#[[2]]&/@e,EdgeWeight->(Last@#&/@Map[Extract[m,#]&,e,{2}])],1,Last@s]/.Thread[s->m]])&

ことに注意してください 文字は転置記号で。そのままMathematicaに貼り付けられます。

使用法:

%@{{2, 55, 5, 3, 1, 1, 4, 1},
  {2, 56, 1, 99, 99, 99, 99, 5},
  {3, 57, 5, 2, 2, 2, 99, 1},
  {3, 58, 4, 2, 8, 1, 99, 2},
  {4, 65, 66, 67, 68, 3, 99, 3},
  {2, 5, 4, 3, 3, 4, 99, 5},
  {75, 76, 77, 78, 79, 80, 81, 2},
  {5, 4, 5, 1, 1, 3, 3, 2}}

出力:

67

設定するg=Graph[...,GraphLayout->{"GridEmbedding","Dimension"->d},VertexLabels->Thread[s->m]p=FindShortestPath[...、次の図にソリューションが視覚的に表示されます(マトリックスの上部がグラフの下部に対応します)。

HighlightGraph[g,PathGraph[p,Thread[Most@p->Rest@p]]]

ここに画像の説明を入力してください


3

Haskell、228バイト

位置は2つの要素のリストです。これらの要素は2 sequenceタプルを使用して簡単に生成でき、パターンマッチングも簡単だからです。

h=g[[-1,-1]]
g t@(p:r)c|p==m=0|1<2=minimum$(sum$concat c):(\q@[a,b]->c!!a!!b+g(q:t)c)#(f(e$s$(\x->[0..x])#m)$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]])where m=[l(c)-1,l(head c)-1]
(#)=map
f=filter
e=flip elem
s=sequence
l=length

開始し-1,-1、各ステップの宛先フィールドのコストをカウントします。

代替の最初の2行:で開始し0,0、出発フィールドをカウントし、マトリックスの次元に等しい座標で終了します(したがって、正当な目的地のリストに追加する必要がある目標から右下)-正確に同じ長さですが、より遅い:

j=i[[0,0]]
i t@(p@[a,b]:r)c|p==m=0|1<2=c!!a!!b+(minimum$(sum$concat c):(\q->i(q:t)c)#(f(e$m:(s$(\x->[0..x-1])#m))$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]]))where m=[l c,l$head c]

infixを使用mapしてもここではバイト数が節約されませんが、コストがかからなくなるとすぐに置き換えられます。これは、より多くの用途で、場合によっては別の括弧の組を削る他の再構築によってのみ改善できるからです。

改善対象:冗長性filter。マージ/イン・ライニングにそれらをfilter(flip elem$(s$(\x->[0..x])#m)\\p)持つimport Data.Listための\\コストを3バイト。

また、の長さ(fromEnumTo 0)はの長さよりも2バイト長すぎます(\x->[0..x])

sum$concat cすべてのフィールドのコストが合計さminimumれるため、空のリストを回避するために与えられるパスコストの簡潔に表現可能な上限です(私のタイプチェッカーは、Integersで動作するものすべてを既に決定しているため、最大値をハードコーディングしないでください、へー)。以前のステップに基づいてステップをどのように制限しても(アルゴリズムの速度が大幅に向上しますが、バイトがかかります)、このフォールバックを必要とする行き止まりを避けることはできません。

  • 1つのフィルターのアイデアは((not.e n).zipWith(-)(head r))、抽出n=s[[-1..1],[-1..1]]を使用すること,[-1,-1]で、初期パスに追加する必要があります。アルゴリズムは、前のステップで既に行っていた可能性のある場所への移動を回避します。これにより、そのエッジに直交するエッジフィールドを踏むことが行き止まりになります。

  • もう1つは((>=0).sum.z(*)d)、を抽出することでしたz=zipWith。これは、再帰および初期の場合のdよう(z(-)p q)に与えられる再帰関数に新しい引数を導入します[1,1]。このアルゴリズムは、負のスカラー積(d前のステップ)で連続するステップを回避します。これは、鋭い45°回転がないことを意味します。これにより、選択肢がかなり絞り込まれ、以前のささいな行き止まりが回避されますが、まだ訪問済みのフィールドに囲まれたパスが残っています(そして、場合によっては急なターンになる「エスケープ」)。


3

パイソン2、356の 320バイト

s=input()
r=lambda x:[x-1,x,x+1][-x-2:]
w=lambda z:[z+[(x,y)]for x in r(z[-1][0])for y in r(z[-1][1])if x<len(s)>0==((x,y)in z)<len(s[0])>y]
l=len(s)-1,len(s[0])-1
f=lambda x:all(l in y for y in x)and x or f([a for b in[l in z and[z]or w(z)for z in x]for a in b])
print min(sum(s[a][b]for(a,b)in x)for x in f([[(0,0)]]))

ここで試してみてください!

notjaganのおかげで-36バイト!

入力としてリストのリストを受け取り、マトリックスを左上から右下にナビゲートするときに最低コストを出力します。

説明

マトリックスの左上から右下まで可能なルートをすべて見つけ、各ルートのx、y座標のリストを作成します。ルートはバックトラックできず、で終了する必要があり(len(s)-1,len(s[0])-1)ます。

座標の各パスで整数を合計し、最小コストを返します。

print簡単に出力する最短経路の座標のリストを変更することができます。


-36バイト、その他の変更あり。
notjagan

@notjagan大きな変更、特にor条件文の使用。ありがとうございました!
溶媒和

1

APL(Dyalog Classic)、33バイト

{⊃⌽,(⊢⌊⍵+(⍉3⌊/⊣/,⊢,⊢/)⍣2)⍣≡+\+⍀⍵}

オンラインでお試しください!

{ } 引数付きの関数

+\+⍀⍵ 行と列で部分的な合計を取り、パス距離の悲観的な上限を確立します

( )⍣≡ 収束するまで繰り返す:

  • (⍉3⌊/⊣/,⊢,⊢/)⍣2隣人までの距離の最小値、つまり2回行う(( )⍣2):左端の列(⊣/,)を自分自身に追加(,⊢/)、右端の列を追加()、水平トリプルの最小値を見つける(3⌊/)および転置(

  • ⍵+ 各ノードの値を隣接ノードとの最小距離に追加します

  • ⊢⌊ 現在の最高距離を破ろうとする

⊃⌽, 最後に、右下のセルを返します

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