ลบตะเข็บผลรวมขั้นต่ำออกจากอาร์เรย์


18

อัลกอริธึมการแกะสลักตะเข็บหรือรุ่นที่ซับซ้อนกว่านั้นใช้สำหรับการปรับขนาดภาพที่รับรู้เนื้อหาในโปรแกรมกราฟิกและไลบรารีต่างๆ มาเล่นกอล์ฟกันเถอะ!

ข้อมูลที่คุณป้อนจะเป็นจำนวนเต็มอาร์เรย์สองมิติ

เอาต์พุตของคุณจะเป็นอาร์เรย์เดียวกันซึ่งแคบลงหนึ่งคอลัมน์โดยที่หนึ่งรายการถูกลบออกจากแต่ละแถวรายการเหล่านั้นจะแทนพา ธ จากบนลงล่างโดยมีผลรวมต่ำสุดของพา ธ ดังกล่าวทั้งหมด

ภาพประกอบแกะสลักตะเข็บ https://en.wikipedia.org/wiki/Seam_carving

ในภาพประกอบด้านบนค่าของแต่ละเซลล์จะแสดงเป็นสีแดง ตัวเลขสีดำคือผลรวมของค่าของเซลล์และหมายเลขสีดำที่ต่ำที่สุดในหนึ่งในสามของเซลล์ด้านบน (ชี้โดยลูกศรสีเขียว) เส้นทางที่ไฮไลต์สีขาวเป็นเส้นทางผลรวมต่ำสุดสองเส้นทางโดยมีผลรวมเป็น 5 (1 + 2 + 2 และ 2 + 2 + 1)

ในกรณีที่มีสองเส้นทางเชื่อมโยงกับผลรวมต่ำสุดไม่สำคัญว่าคุณจะลบออก

อินพุตควรถูกนำมาจาก stdin หรือเป็นพารามิเตอร์ฟังก์ชัน สามารถจัดรูปแบบในลักษณะที่สะดวกกับภาษาที่คุณเลือกรวมถึงเครื่องหมายวงเล็บและ / หรือตัวคั่น โปรดระบุคำตอบของคุณว่าจะให้นำเข้าข้อมูลอย่างไร

เอาต์พุตควรเป็น stdout ในรูปแบบที่มีการคั่นอย่างไม่น่าเชื่อหรือเป็นค่าส่งคืนฟังก์ชันในภาษาของคุณเทียบเท่ากับอาร์เรย์ 2d (ซึ่งอาจรวมถึงรายการที่ซ้อนกัน ฯลฯ )

ตัวอย่าง:

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)

แก้ไข: ตัวเลขทั้งหมดจะไม่เป็นลบและทุกตะเข็บที่เป็นไปได้จะมีผลรวมที่พอดีกับจำนวนเต็ม 32 บิตที่ลงนามแล้ว


ในตัวอย่างค่าของเซลล์ทั้งหมดเป็นตัวเลขหลักเดียว รับประกันได้หรือไม่ ถ้าไม่มีข้อสมมติฐานอื่น ๆ ที่สามารถทำได้เกี่ยวกับขนาด / ช่วงของค่าหรือไม่ ตัวอย่างเช่นผลรวมเหมาะกับค่า 16/32 บิต? หรืออย่างน้อยก็ค่าทั้งหมดเป็นค่าบวก?
Reto Koradi

@RetoKoradi แก้ไขโดยมีรายละเอียดเกี่ยวกับช่วง
Sparr

คำตอบ:


5

CJam, 51 44 ไบต์

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

นี่คือฟังก์ชั่นที่ไม่ระบุชื่อที่ปรากฏอาร์เรย์ 2D จากสแต็กและส่งกลับหนึ่งรายการ

ลองกรณีทดสอบออนไลน์ในล่าม CJam 1

ความคิด

วิธีการนี้จะวนซ้ำไปตามองค์ประกอบที่เป็นไปได้ทั้งหมดขององค์ประกอบแถวกรองสิ่งที่ไม่สอดคล้องกับตะเข็บเรียงตามผลรวมที่สอดคล้องกันเลือกค่าต่ำสุดและลบองค์ประกอบที่เกี่ยวข้องออกจากอาร์เรย์ 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 โปรดทราบว่า CJam ไม่สามารถแยกแยะความแตกต่างระหว่างอาร์เรย์ว่างและสตริงว่างเนื่องจากสตริงเป็นเพียงอาร์เรย์ที่มีองค์ประกอบเป็นอักขระ ""ดังนั้นการแสดงสตริงของทั้งสองอาร์เรย์ที่ว่างเปล่าและสตริงที่ว่างเปล่า

2 ในขณะที่เวลาซับซ้อนของขั้นตอนวิธีการที่แสดงบนหน้าวิกิพีเดียควรจะเป็นO (นาโนเมตร)สำหรับn ×มเมทริกซ์ของคนนี้เป็นอย่างน้อยO (m n )



น่าเศร้าที่ใช้ไม่ได้กับกรณีทดสอบครั้งที่สอง ฉันได้ยื่นรายงานข้อผิดพลาดเมื่อประมาณสองสัปดาห์ที่แล้ว
เดนนิส

5

Haskell, 187 ไบต์

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

ตัวอย่างการใช้งาน:

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

มันทำงานอย่างไรรุ่นสั้น: สร้างรายการของทุกเส้นทาง (1) ต่อเส้นทาง: ลบองค์ประกอบที่เกี่ยวข้อง (2) และรวมองค์ประกอบที่เหลือทั้งหมด (3) ใช้สี่เหลี่ยมที่มีผลรวมที่ใหญ่ที่สุด (4)

รุ่นที่ยาวกว่า:

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 ไบต์

ฉันแน่ใจว่านี่จะไม่ชนะเพราะมันยาว แต่นี่เป็นวิธีแก้ปัญหาที่ตรงไปตรงมา:

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

เราสร้างอาร์เรย์พลังงานซ้ำแล้วซ้ำอีกและติดตามทิศทางที่ตะเข็บไปจากนั้นสร้างรายการลบเมื่อเราทราบตำแหน่งสุดท้าย ลบตะเข็บผ่านการจัดทำดัชนี 1D จากนั้นกลับสู่อาร์เรย์ด้วยมิติใหม่


3
โอ้พระเจ้า ... ฉันคิดว่าฉันเพิ่งเห็น IDL เล็กน้อย (อีกครั้ง) ฉันคิดว่าฉันได้เห็นอย่างนั้นหลังจากสำเร็จการศึกษา ...
Kyle Kanos

ที่กล่าวว่าฉันสงสัยว่าสิ่งนี้ยังใช้งานได้กับ GDL เพื่อให้ผู้คนไม่เต็มใจที่จะจ่าย 1 พันล้านดอลลาร์สำหรับใบอนุญาตผู้ใช้รายเดียวสามารถทดสอบได้หรือไม่
Kyle Kanos

ฉันไม่เคยใช้ GDL ดังนั้นฉันจึงไม่สามารถพูดได้ (จริงๆแล้วฉันลืมมันไป) สิ่งเดียวที่อาจทำให้เกิดปัญหาคือถ้า GDL ไม่สามารถจัดการกับการสร้างอาร์เรย์ของไวยากรณ์[0:n]; ว่าที่จริงแล้วมันเป็นเรื่องง่ายที่จะเปลี่ยนด้วยr+=[0:z[1]-1]*z[0] r+=indgen(z[1]-1)*z[0]
sirpercival

นอกจากนี้ในขณะที่ฉันต้องการใช้หลามสำหรับกอล์ฟของฉันไม่มีใครทำ IDL ดังนั้นฉันจึงรู้สึกว่าจำเป็นต้องมีส่วนร่วมกับ XD นอกจากนี้มันทำบางสิ่งได้ดีมาก
sirpercival

3
ฉันทำให้ฉันรู้สึกหงุดหงิด / ร้องไห้ได้เป็นอย่างดี;)
Kyle Kanos

3

JavaScript ( ES6 ) 197 209 215

การใช้อัลกอริทึม wikipedia แบบทีละขั้นตอน

อาจจะสั้นกว่านี้อีก

ทดสอบการเรียกใช้ตัวอย่างข้อมูลใน 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 ไบต์

นี่จะไม่ชนะรางวัลใด ๆ แต่ฉันสนุกกับการทำงาน ช่องว่างสำหรับเหตุผลด้านความงามเท่านั้นและไม่รวมอยู่ในจำนวนไบต์

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

รหัสนี้กำหนดฟังก์ชั่นที่ไม่ระบุชื่อซึ่งมีอาร์กิวเมนต์และค่าส่งคืนเป็นรายการซ้อนกัน มันใช้อัลกอริทึมจากหน้า Wikipedia: a(อาร์กิวเมนต์) คือตัวเลขสีแดงและzเป็นตัวเลขสีดำ

นี่คือรุ่นที่มีสายรัดทดสอบ:

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:\> pip.py minSumSeam.pip -p
[[4;3;5;2];[3;5;2;3];[5;4;2;1]]
[[2;3;4;5]]
[[];[];[]]

และนี่ก็เทียบเท่าคร่าวๆใน Python 3 ถ้าใครต้องการคำอธิบายที่ดีกว่าของรหัส Pip เพียงแค่ถามในความคิดเห็น

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.