Loại bỏ đường nối tổng nhỏ từ một mảng


18

Thuật toán khắc đường may, hoặc một phiên bản phức tạp hơn của nó, được sử dụng để thay đổi kích thước hình ảnh nhận biết nội dung trong các chương trình và thư viện đồ họa khác nhau. Hãy chơi gôn nào!

Đầu vào của bạn sẽ là một mảng số nguyên hai chiều hình chữ nhật.

Đầu ra của bạn sẽ là cùng một mảng, hẹp hơn một cột, với một mục được xóa khỏi mỗi hàng, các mục đó biểu thị một đường dẫn từ trên xuống dưới với tổng thấp nhất của tất cả các đường dẫn đó.

Seam khắc minh họa https://en.wikipedia.org/wiki/Seam_carving

Trong hình minh họa ở trên, giá trị của mỗi ô được hiển thị bằng màu đỏ. Các số màu đen là tổng của giá trị của một ô và số màu đen thấp nhất trong một trong ba ô phía trên nó (được chỉ bởi các mũi tên màu xanh lá cây). Các đường dẫn được tô sáng màu trắng là hai đường dẫn tổng thấp nhất, cả hai đều có tổng bằng 5 (1 + 2 + 2 và 2 + 2 + 1).

Trong trường hợp có hai đường dẫn được buộc với tổng thấp nhất, việc bạn loại bỏ không thành vấn đề.

Đầu vào nên được lấy từ stdin hoặc như một tham số chức năng. Nó có thể được định dạng theo cách thuận tiện cho ngôn ngữ bạn chọn, bao gồm dấu ngoặc và / hoặc dấu phân cách. Vui lòng xác định trong câu trả lời của bạn như thế nào đầu vào dự kiến.

Đầu ra phải xuất ra ở định dạng được phân tách rõ ràng hoặc dưới dạng giá trị trả về hàm trong ngôn ngữ của bạn tương đương với mảng 2d (có thể bao gồm các danh sách lồng nhau, v.v.).

Ví dụ:

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: Tất cả các số sẽ không âm và mọi đường may có thể sẽ có một tổng phù hợp với số nguyên 32 bit đã ký.


Trong các ví dụ, tất cả các giá trị ô là số có một chữ số. Điều đó có được đảm bảo không? Nếu không, có giả định nào khác có thể được thực hiện về kích thước / phạm vi của các giá trị không? Ví dụ: tổng phù hợp với giá trị 16/32 bit? Hoặc ít nhất là tất cả các giá trị là tích cực?
Reto Koradi

@RetoKoradi chỉnh sửa với các chi tiết về phạm vi
Sparr

Câu trả lời:


5

CJam, 51 44 byte

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

Đây là một hàm ẩn danh bật ra một mảng 2D từ ngăn xếp và trả lại một mảng.

Hãy thử các trường hợp thử nghiệm trực tuyến trong trình thông dịch CJam . 1

Ý kiến

Cách tiếp cận này lặp lại trên tất cả các kết hợp có thể của các phần tử hàng, lọc ra các phần tử không tương ứng với các đường nối, sắp xếp theo tổng tương ứng, chọn mức tối thiểu và loại bỏ các phần tử tương ứng khỏi mảng. 2

_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 Lưu ý rằng CJam không thể phân biệt giữa mảng trống và chuỗi rỗng, vì chuỗi chỉ là mảng có thành phần là ký tự. Do đó, biểu diễn chuỗi của cả mảng trống và chuỗi rỗng là "".

2 Mặc dù độ phức tạp thời gian của thuật toán hiển thị trên trang Wikipedia phải là O (nm) cho ma trận n × m , nhưng thuật toán này ít nhất là của O (m n ) .


{2ew::m2f/0-!},
Trình tối ưu hóa

Đáng buồn thay, điều đó sẽ không làm việc cho trường hợp thử nghiệm thứ hai. Tôi đã nộp một báo cáo lỗi về điều này hai tuần trước.
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

Ví dụ sử dụng:

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

Cách thức hoạt động, phiên bản ngắn: xây dựng danh sách tất cả các đường dẫn (1), mỗi đường dẫn: loại bỏ các yếu tố tương ứng (2) và tổng hợp tất cả các yếu tố còn lại (3). Lấy hình chữ nhật có tổng lớn nhất (4).

Phiên bản dài hơn:

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 byte

Meh, tôi chắc chắn điều này sẽ không thắng vì nó dài, nhưng đây là một giải pháp đơn giản:

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

Ung dung:

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

Chúng tôi lặp đi lặp lại tạo ra mảng năng lượng và theo dõi hướng đi của đường may, sau đó xây dựng một danh sách loại bỏ một khi chúng ta biết vị trí cuối cùng. Xóa đường may thông qua lập chỉ mục 1D, sau đó cải tổ lại thành mảng với kích thước mới.


3
Trời ơi ... tôi nghĩ rằng tôi đã ném lên một chút khi nhìn thấy IDL (một lần nữa). Tôi nghĩ rằng tôi đã hoàn thành việc đó sau khi tốt nghiệp ...
Kyle Kanos

Điều đó nói rằng, tôi nghi ngờ điều này cũng hoạt động cho GDL, để những người không sẵn sàng trả 1 tỷ đô la cho giấy phép một người dùng có thể kiểm tra nó?
Kyle Kanos

Tôi chưa bao giờ sử dụng GDL, vì vậy tôi không thể nói (thật lòng tôi đã quên nó tồn tại). Điều duy nhất có thể gây ra vấn đề là nếu GDL không thể xử lý việc tạo cú pháp mảng [0:n]; nếu đó là sự thật, thì đó là dễ dàng để thay thế r+=[0:z[1]-1]*z[0]với r+=indgen(z[1]-1)*z[0].
sirpercival

Ngoài ra, trong khi tôi muốn sử dụng python cho golf của mình, không ai khác làm IDL nên tôi cảm thấy bắt buộc phải đóng góp XD. Thêm vào đó, nó làm một số điều rất tốt.
sirpercival

3
Tôi làm cho tôi co rúm / khóc rất tốt;)
Kyle Kanos

3

JavaScript ( ES6 ) 197 209 215

Từng bước thực hiện thuật toán wikipedia.

Có lẽ có thể rút ngắn hơn.

Kiểm tra chạy đoạn mã trong 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>


1

Pip, 91 byte

Điều này sẽ không giành được bất kỳ giải thưởng nào, nhưng tôi rất vui khi làm việc với nó. Khoảng trắng chỉ dành cho lý do mỹ phẩm và không được bao gồm trong số 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
}

Mã này xác định một hàm ẩn danh có đối số và giá trị trả về là các danh sách lồng nhau. Nó thực hiện thuật toán từ trang Wikipedia: a(đối số) là các số màu đỏ vàz là số màu đen.

Đây là phiên bản có khai thác thử nghiệm:

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)

Các kết quả:

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

Và đây là tương đương thô trong Python 3. Nếu bất cứ ai muốn giải thích rõ hơn về mã Pip, chỉ cần hỏi trong các nhận xét.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.