Hapus jahitan jumlah mininum dari array

18

Algoritma ukiran jahitan, atau versi yang lebih kompleks, digunakan untuk mengubah ukuran gambar yang sadar konten di berbagai program grafik dan pustaka. Mari kita bermain golf!

Input Anda akan berupa array bilangan bulat dua dimensi persegi panjang.

Output Anda akan menjadi array yang sama, satu kolom lebih sempit, dengan satu entri dihapus dari setiap baris, entri-entri itu mewakili jalur dari atas ke bawah dengan jumlah terendah dari semua jalur tersebut.

Ilustrasi ukiran jahitan https://en.wikipedia.org/wiki/Seam_carving

Dalam ilustrasi di atas, nilai setiap sel ditampilkan dalam warna merah. Angka hitam adalah jumlah dari nilai sel dan angka hitam terendah di salah satu dari tiga sel di atasnya (ditunjukkan oleh panah hijau). Jalur yang disorot putih adalah dua jalur jumlah terendah, keduanya dengan jumlah 5 (1 + 2 + 2 dan 2 + 2 + 1).

Dalam kasus di mana ada dua jalur terikat untuk jumlah terendah, tidak masalah yang Anda hapus.

Input harus diambil dari stdin atau sebagai parameter fungsi. Ini dapat diformat dengan cara yang sesuai dengan bahasa pilihan Anda, termasuk tanda kurung dan / atau pembatas. Silakan tentukan dalam jawaban Anda bagaimana input diharapkan.

Output harus stdout dalam format terbatas tegas, atau sebagai fungsi mengembalikan nilai dalam bahasa Anda setara dengan array 2d (yang mungkin termasuk daftar bersarang, dll).

Contoh:

Input:
1 4 3 5 2
3 2 5 2 3
5 2 4 2 1
Output:
4 3 5 2      1 4 3 5
3 5 2 3  or  3 2 5 3
5 4 2 1      5 2 4 2

Input:
1 2 3 4 5
Output:
2 3 4 5

Input:
1
2
3
Output:
(empty, null, a sentinel non-array value, a 0x3 array, or similar)

EDIT: Angka-angka semua akan non-negatif, dan setiap jahitan yang mungkin akan memiliki jumlah yang cocok dengan integer 32 bit yang ditandatangani.

Sparr
sumber
Dalam contoh, semua nilai sel adalah angka satu digit. Apakah itu dijamin? Jika tidak, adakah asumsi lain yang dapat dibuat tentang ukuran / kisaran nilai? Misalnya jumlah tersebut cocok dengan nilai 16/32-bit? Atau setidaknya semua nilai positif?
Reto Koradi
@RetoKoradi diedit dengan detail kisaran
Sparr

Jawaban:

5

CJam, 51 44 byte

{_z,1$,m*{_1>.-W<2f/0-!},{1$.=:+}$0=.{WtW-}}

Ini adalah fungsi anonim yang memunculkan array 2D dari tumpukan dan mendorongnya sebagai balasan.

Coba uji kasus secara online di juru bahasa CJam . 1

Ide

Pendekatan ini mengulangi semua kemungkinan kombinasi elemen baris, memfilter kombinasi yang tidak sesuai dengan jahitan, mengurutkan berdasarkan jumlah yang sesuai, memilih minimum dan menghapus elemen yang sesuai dari array. 2

Kode

_z,   e# Get the length of the transposed array. Pushes the number of columns (m).
1$,   e# Get the length of the array itself. Pushes the number of rows (n).
m*    e# Cartesian power. Pushes the array of all n-tuples with elements in [0 ... m-1].
{     e# Filter:
  _1> e#     Push a copy of the tuple with first element removed.
  .-  e#     Vectorized difference.
  W<  e#     Discard last element.
  2f/ e#     Divide all by 2.
  0-  e#     Remove 0 from the results.
  !   e#     Push 1 if the remainder is empty and 0 otherwise.
},    e#     Keep only tuples which pushed a 1.

      e# The filtered array now contains only tuples that encode valid paths of indexes.

{     e# Sort by:
  1$  e#     Copy the input array.
  .=  e#     Retrieve the element of each row that corresponds to the index in the tuple.
  :+  e#     Add all elements.
}$    e#
0=    e# Retrieve the tuple of indexes with minimum sum.
.{    e# For each row in the array and the corresponding index in the tuple:
  Wt  e#     Replace the element at that index with -1.
  W-  e#     Remove -1 from the row.
}

1 Perhatikan bahwa CJam tidak dapat membedakan antara array kosong dan string kosong, karena string hanyalah array yang elemen-elemennya adalah karakter. Dengan demikian, representasi string dari kedua array kosong dan string kosong adalah "".

2 Sementara kompleksitas waktu dari algoritma yang ditampilkan pada halaman Wikipedia harus dari O (nm) untuk matriks n × m , yang ini setidaknya dari O (m n ) .

Dennis
sumber
{2ew::m2f/0-!},
Pengoptimal
Sayangnya, itu tidak akan berfungsi untuk test case kedua. Saya telah mengajukan laporan bug tentang ini dua minggu lalu.
Dennis
5

Haskell, 187 byte

l=length
f a@(b:c)=snd$maximum$(zip=<<map(sum.concat))$map(zipWith((uncurry((.drop 1).(++)).).flip splitAt)a)$iterate((\e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e])=<<)[[y]|y<-[0..l b-1]]!!l c

Contoh penggunaan:

*Main> f [[1,4,3,5,2],[3,2,5,2,3],[5,2,4,2,1]]
[[4,3,5,2],[3,5,2,3],[5,4,2,1]]

*Main> f [[1],[2],[3]]
[[],[],[]]

*Main> f [[1,2,3,4,5]]
[[2,3,4,5]]

Cara kerjanya, versi singkat: buat daftar semua jalur (1), per jalur: hapus elemen yang sesuai (2) dan jumlah semua elemen yang tersisa (3). Ambil kotak dengan jumlah terbesar (4).

Versi yang lebih panjang:

Input parameters, assigned via pattern matching:
a = whole input, e.g. [[1,2,4],[2,5,6],[3,1,6]]
b = first line, e.g. [1,2,4]
c = all lines, except first, e.g. [[2,5,6],[3,1,6]]

Step (1), build all paths:

iterate((\e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e])=<<)[[y]|y<-[0..l b-1]]!!l c

     [[y]|y<-[0..l b-1]]           # build a list of single element lists
                                   # for all numbers from 0 to length b - 1
                                   # e.g. [[0],[1],[2]] for a 3 column input.
                                   # These are all possible start points

     \e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e]
                                   # expand a list of paths by replacing each
                                   # path with 3 new paths (up-left, up, up-right)

     (...)=<<                      # flatten the list of 3-new-path lists into
                                   # a single list

     iterate (...) [...] !! l c    # repeatedly apply the expand function to
                                   # the start list, all in all (length c) times.


Step (2), remove elements

map(zipWith((uncurry((.drop 1).(++)).).flip splitAt)a)

     (uncurry((.drop 1).(++)).).flip splitAt
                                   # point-free version of a function that removes
                                   # an element at index i from a list by
                                   # splitting it at index i, and joining the
                                   # first part with the tail of the second part

      map (zipWith (...) a) $ ...  # per path: zip the input list and the path with
                                   # the remove-at-index function. Now we have a list
                                   # of rectangles, each with a path removed

Step (3), sum remaining elements

zip=<<map(sum.concat)             # per rectangle: build a pair (s, rectangle)
                                  # where s is the sum of all elements


Step (4), take maximum

snd$maximum                      # find maximum and remove the sum part from the
                                 # pair, again.
nimi
sumber
3

IDL 8.3, 307 byte

Meh, saya yakin ini tidak akan menang karena ini panjang, tapi di sini ada solusi langsung:

pro s,a
z=size(a,/d)
if z[0]lt 2then return
e=a
d=a*0
u=max(a)+1
for i=0,z[1]-2 do begin
e[*,i+1]+=min([[u,e[0:-2,i]],[e[*,i]],[e[1:*,i],u]],l,d=2)
d[*,i]=l/z[0]-1
endfor
v=min(e[*,-1],l)
r=intarr(z[1])+l
for i=z[1]-2,0,-1 do r[0:i]+=d[r[i+1],i]
r+=[0:z[1]-1]*z[0]
remove,r,a
print,reform(a,z[0]-1,z[1])
end

Tidak Disatukan:

pro seam, array
  z=size(array, /dimensions)
  if z[0] lt 2 then return
  energy = array
  ind = array * 0
  null = max(array) + 1
  for i=0, z[1]-2 do begin
    energy[*, i+1] += min([[null, energy[0:-2,i]], [energy[*,i]], [energy[1:*,i], null]], loc ,dimension=2)
    ind[*, i] = loc / z[0] - 1
  endfor
  void = min(energy[*,-1], loc)
  rem = intarr(z[1]) + loc
  for i=z[1]-2, 0, -1 do rem[0:i] += ind[rem[i+1], i]
  rem += [0:z[1]-1]*z[0]
  remove, rem, array
  print, reform(array, z[0]-1, z[1])
end

Kami secara iteratif membuat susunan energi dan melacak ke arah mana lapisan berjalan, kemudian menyusun daftar pelepasan setelah kami mengetahui posisi akhir. Hapus jahitan melalui pengindeksan 1D, lalu reformasi kembali ke array dengan dimensi baru.

Sirpercival
sumber
3
Ya Tuhan ... saya pikir saya hanya muntah melihat IDL (lagi). Saya pikir saya sudah selesai melihat itu setelah lulus ...
Kyle Kanos
Yang mengatakan, saya menduga ini juga berfungsi untuk GDL, sehingga orang-orang yang tidak mau membayar $ 1 miliar untuk lisensi pengguna tunggal dapat mengujinya?
Kyle Kanos
Saya tidak pernah menggunakan GDL, jadi saya tidak bisa mengatakan (jujur ​​saya lupa itu ada). Satu-satunya hal yang dapat menyebabkan masalah adalah jika GDL tidak dapat menangani pembuatan array sintaks [0:n]; jika itu benar, maka mudah untuk mengganti r+=[0:z[1]-1]*z[0]dengan r+=indgen(z[1]-1)*z[0].
sirpercival
Juga, sementara saya lebih suka menggunakan python untuk golf saya, tidak ada orang lain yang melakukan IDL jadi saya merasa berkewajiban untuk berkontribusi XD. Plus, itu melakukan beberapa hal dengan sangat baik.
sirpercival
3
Saya memang membuat saya ngeri / menangis dengan sangat baik;)
Kyle Kanos
3

JavaScript ( ES6 ) 197 209 215

Langkah demi langkah implementasi algoritma wikipedia.

Mungkin bisa dipersingkat lagi.

Tes menjalankan cuplikan di Firefox.

// Golfed

F=a=>(u=>{for(r=[i=p.indexOf(Math.min(...p))];l--;i=u[l][i])(r[l]=[...a[l]]).splice(i,1)})
(a.map(r=>[r.map((v,i)=>(q[i]=v+~~p[j=p[i+1]<p[j=p[i-1]<p[i]?i-1:i]?i+1:j],j),q=[++l]),p=q][0],p=[l=0]))||r

// LESS GOLFED

U=a=>{
  p = []; // prev row
  u = a.map( r => { // in u the elaboration result, row by row
      q=[];
      t = r.map((v,i) => { // build a row for u from a row in a
        j = p[i-1] < p[i] ? i-1 : i; // find position of min in previous row
        j = p[i+1] < p[j] ? i+1 : j;
        q[i] = v + ~~p[j]; // values for current row
        // ~~ convert to number, as at first row all element in p are 'undefined'
        return j;//  position in u, row by row
      });
      p = q; // current row becomes previous row 
      return t;
  });
  n = Math.min(...p) // minimum value in the last row
  i = p.indexOf(n); // position of minimum (first if there are more than one present)
  r = []; // result      
  // scan u bottom to up to find the element to remove in the output row
  for(j = u.length; j--;)
  {
    r[j] = a[j].slice(); // copy row to output
    r[j].splice(i,1); // remove element
    i = u[j][i]; // position for next row
  }
  return r;
}

// TEST        
out=x=>O.innerHTML += x + '\n';        

test=[
  [[1,4,3,5,2],[3,2,5,2,3],[5,2,4,2,1]],
  [[1,2,3,4,5]],
  [[1],[2],[3],[4]]
];  

test.forEach(t=>{
  out('Test data:\n' + t.map(v=>'['+v+']').join('\n'));
  r=F(t);
  out('Golfed version:\n' + r.map(v=>'['+v+']').join('\n'))      
  r=U(t);
  out('Ungolfed version:\n' + r.map(v=>'['+v+']').join('\n'))
})  
<pre id=O></pre>

edc65
sumber
1

Pip, 91 byte

Ini tidak akan memenangkan hadiah apa pun, tetapi saya senang mengerjakannya. Whitespace hanya untuk alasan kosmetik dan tidak termasuk dalam jumlah byte.

{
 p:{(zaj-1+,3RMv)}
 z:a
 w:,#(a0)
 Fi,#a
  Fjw
   Ii
    z@i@j+:MN(pi-1)
 s:z@i
 Ti<0{
  j:s@?MNs
  a@i@:wRMj
  s:(p--i)
 }
 a
}

Kode ini mendefinisikan fungsi anonim yang argumen dan nilai baliknya adalah daftar bersarang. Ini mengimplementasikan algoritma dari halaman Wikipedia: a(argumen) adalah angka merah, dan zadalah angka hitam.

Inilah versi dengan test harness:

f:{p:{(zaj-1+,3RMv)}z:aw:,#(a0)Fi,#aFjwIiz@i@j+:MN(pi-1)s:z@iTi<0{j:s@?MNsa@i@:wRMjs:(p--i)}a}
d:[
 [[1 4 3 5 2]
  [3 2 5 2 3]
  [5 2 4 2 1]]
 [[1 2 3 4 5]]
 [[1]
  [2]
  [3]]
 ]
Fld
 P(fl)

Hasil:

C:\> pip.py minSumSeam.pip -p
[[4;3;5;2];[3;5;2;3];[5;4;2;1]]
[[2;3;4;5]]
[[];[];[]]

Dan inilah persamaan kasar dalam Python 3. Jika ada yang menginginkan penjelasan yang lebih baik dari kode Pip, tanyakan di komentar.

def f(a):
    z = [row.copy() for row in a]
    w = range(len(a[0]))

    for i in range(len(a)):
        for j in w:
            if i:
                z[i][j] += min(z[i-1][max(j-1,0):j+2])
    s = z[i]
    while i >= 0:
        j = s.index(min(s))
        del a[i][j]
        i -= 1
        s = z[i][max(j-1,0):j+2]
    return a
DLosc
sumber