Mininum sum dikişini bir diziden kaldırma


18

Dikiş oyma algoritması veya daha karmaşık bir sürümü, çeşitli grafik programlarında ve kitaplıklarda içeriğe duyarlı görüntü için kullanılır. Hadi golf oynayalım!

Girişiniz dikdörtgen iki boyutlu bir tamsayı dizisi olacaktır.

Çıktınız aynı dizi olacak, bir sütun daha dar olacak ve her satırdan bir giriş kaldırılacak, bu girişler yukarıdan aşağıya bir yolu temsil edecek ve bu yolların en düşük toplamına sahip olacak.

Dikiş oyma illüstrasyon https://en.wikipedia.org/wiki/Seam_carving

Yukarıdaki çizimde, her bir hücrenin değeri kırmızı ile gösterilmiştir. Siyah sayılar, bir hücrenin değerinin ve üzerindeki üç hücreden birindeki (yeşil oklarla gösterilen) en düşük siyah sayının toplamıdır. Vurgulanan beyaz yollar, her ikisi de 5 (1 + 2 + 2 ve 2 + 2 + 1) olmak üzere en düşük iki toplam yoldur.

En düşük toplam için iki yolun bağlı olduğu durumlarda, hangisini kaldırdığınız önemli değildir.

Giriş stdin'den veya bir fonksiyon parametresi olarak alınmalıdır. Köşeli ayraçlar ve / veya sınırlayıcılar dahil, dilinize uygun bir şekilde biçimlendirilebilir. Lütfen cevabınızda girdinin nasıl beklendiğini belirtin.

Çıktı, açıkça sınırlandırılmış bir biçimde veya dilinizin 2d dizisine (iç içe listeler, vb. İçerebilir) eşdeğer bir işlev dönüş değeri olarak stdout olmalıdır.

Örnekler:

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)

DÜZENLEME: Sayıların tümü negatif olmayacak ve olası tüm dikişlerin işaretli bir 32 bit tamsayıya uyan bir toplamı olacaktır.


Örneklerde, tüm hücre değerleri tek haneli sayılardır. Bu garantili mi? Değilse, değerlerin boyutu / aralığı hakkında yapılabilecek başka varsayımlar var mı? Örneğin, toplam 16/32-bit değere uyuyor mu? Ya da en azından tüm değerler pozitif mi?
Reto Koradi

@RetoKoradi aralıktaki ayrıntılarla düzenlendi
Sparr

Yanıtlar:


5

CJam, 51 44 bayt

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

Bu, yığından bir 2D dizi çıkaran ve karşılığında bir diziyi iten anonim bir işlevdir.

CJam yorumlayıcısında test senaryolarını çevrimiçi deneyin . 1

Fikir

Bu yaklaşım, satır öğelerinin tüm olası kombinasyonlarını yineler, dikişlere karşılık gelmeyenleri filtreler, karşılık gelen toplamla sıralar, minimum değeri seçer ve karşılık gelen öğeleri diziden kaldırır. 2

kod

_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 CJam'ın boş diziler ve boş dizeler arasında ayrım yapamayacağına dikkat edin, çünkü dizeler sadece elemanları karakter olan dizilerdir. Böylece, hem boş dizilerin hem de boş dizelerin dize gösterimi olur "".

2 Wikipedia sayfasında gösterilen algoritmanın zaman karmaşıklığı bir n × m matrisi için O (nm) olmalıdır, ancak bu en azından O ( mn ) 'dir .


{2ew::m2f/0-!},
Optimize Edici

Ne yazık ki, bu ikinci test vakası için işe yaramaz. Bu konuda iki hafta önce bir hata raporu hazırladım .
Dennis

5

Haskell, 187 bayt

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

Kullanım örneği:

*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]]

Nasıl çalışır, kısa sürüm: yol başına tüm yolların (1) bir listesini oluşturun: karşılık gelen öğeleri (2) kaldırın ve kalan tüm öğeleri (3) toplayın. En büyük toplamı (4) olan dikdörtgeni alın.

Daha uzun versiyon:

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.

3

IDL 8.3, 307 bayt

Meh, eminim bu kazanmayacak çünkü uzun, ama burada basit bir çözüm var:

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

Ungolfed:

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

Enerji dizisini yinelemeli olarak yaratırız ve dikişin hangi yöne gittiğini izleriz, ardından son konumu bildiğimizde bir kaldırma listesi oluştururuz. Dikişi 1D indeksleme yoluyla çıkarın, ardından yeni boyutlarla diziye geri reform yapın.


3
Tanrım ... Sanırım sadece biraz görerek IDL attım (tekrar). Mezun olduktan sonra bunu gördüğümü sanıyordum ...
Kyle Kanos

Bununla birlikte, bunun GDL için de çalıştığından şüpheleniyorum, böylece tek kullanıcı lisansı için 1 milyar dolar ödemek istemeyen insanlar test edebilir mi?
Kyle Kanos

GDL'yi hiç kullanmadım, bu yüzden söyleyemem (dürüstçe var olduğunu unuttum). Soruna neden olabilecek tek şey, GDL'nin sözdiziminin dizi oluşturma işlemlerini gerçekleştirememesi [0:n]; Bu doğruysa, o zaman değiştirmek kolaydır r+=[0:z[1]-1]*z[0]ile r+=indgen(z[1]-1)*z[0].
sirperkival

Ayrıca, golflerim için python kullanmayı tercih ederken, hiç kimse IDL yapmaz, bu yüzden XD'ye katkıda bulunmak zorunda hissediyorum. Ayrıca, bazı şeyleri çok iyi yapıyor.
sirperkival

3
Beni çok iyi bir şekilde ağlatıyorum / ağlatıyorum;)
Kyle Kanos

3

JavaScript ( ES6 ) 197209 215

Wikipedia algoritmasının adım adım uygulanması.

Muhtemelen daha fazla kısaltılabilir.

Snippet'in Firefox'ta çalıştırılmasını test edin.

// 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>


1

Pip, 91 bayt

Bu hiçbir ödül kazanmaz, ama üzerinde çalışırken çok eğlendim. Beyaz alan yalnızca kozmetik nedenlerle kullanılır ve bayt sayısına dahil değildir.

{
 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
}

Bu kod, argümanı ve dönüş değeri iç içe listeler olan anonim bir işlevi tanımlar. Wikipedia sayfasından algoritmayı uygular: a(bağımsız değişken) kırmızı sayılar ve zsiyah sayılardır.

İşte test kayışına sahip bir versiyon:

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)

Sonuçlar:

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

Ve işte Python 3'teki kaba eşdeğer. Pip kodunun daha iyi bir açıklamasını isteyen varsa, sadece yorumlarda sorun.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.