2017阿里內推筆試題--演算法工程師(運籌優化)
2017阿里內推筆試題–演算法工程師(運籌優化)
題目
沐哲是一個菜鳥倉庫的一個揀貨員,但他有非常個怪異的習慣。每次揀貨的重量都要比之前揀的一個輕,每次揀到貨後都可以得到1塊錢,沐哲想知道這樣最多能賺多少錢
32 34 7 33 21 2
13 12 3 11 26 36
16 30 22 1 24 14
20 23 25 5 19 29
27 15 9 17 31 4
6 18 8 10 35 28
沐哲可以從倉庫的某個貨架開始揀貨,下一步可以往上走,也可以往下走,當然,向左向右也可以,但必須使得下一個貨物重量減小,才會去揀。在上面的倉庫中,一條可揀貨的路徑為 25-22-3。當然30-23-20-16-13-12-3可以揀的貨更多。這也是賺錢最多的一條路徑。
要求
輸入行數、列數和資料矩陣,輸出所賺的最大錢數。
例子:
輸入:
6 6
32 34 7 33 21 2
13 12 3 11 26 36
16 30 22 1 24 14
20 23 25 5 19 29
27 15 9 17 31 4
6 18 8 10 35 28
輸出:
7
分析
此題應該屬於樹搜尋的題目,拿到題目後一直在想怎麼用動態規劃。結果測試時間都過了,還是沒做出來,真是搓的一筆。
最暴力的解法就是對每個點遍歷,以每個點為起點進行深度優先搜尋,找它相鄰點的可行路徑。搜尋後將最大結果儲存到當前位置,等遍歷完後,找到矩陣元素的最大值,然後輸出該值。
版本一
函式:
# check the (i,j) is in the matrix index
def isInside(row,col,i,j):
return (i in range(row)) and (j in range(col))
# get the max step from the current point
def currentMaxStep(data,row,col,i,j):
max_step=0
directs = [(-1,0),(1,0),(0,-1),(0,1)]
for (dx,dy) in directs:
x,y = i+dx,j+dy
if (isInside(row,col,x,y) and data[x][y] < data[i][j]):
max_step = max([currentMaxStep(data,row,col,x,y),max_step])
return max_step + 1
# traverse the whole data and generate the max step map
def getMaxMap(data,row,col):
Map = [[0 for j in range(col)] for i in range(row)]
for i in range(row):
for j in range(col):
Map[i][j] = currentMaxStep(data,row,col,i,j)
print('the max step map is:')
for i in range(row):
print(Map[i])
return Map
# find the max from the max step map
def maxStep(data,row,col):
Map = getMaxMap(data,row,col)
return max([max(i) for i in Map])
測試結果:
if __name__=='__main__':
row,col = 6,6
data = [[32, 34, 7, 33, 21, 2],
[13, 12, 3, 11, 26, 36],
[16, 30, 22, 1, 24, 14],
[20, 23, 25, 5, 19, 29],
[27, 15, 9, 17, 31, 4],
[ 6, 18, 8, 10, 35, 28]]
print(maxStep(data,row,col))
輸出:
the max step map is:
[4, 5, 2, 3, 2, 1]
[3, 2, 1, 2, 5, 6]
[4, 7, 2, 1, 4, 1]
[5, 6, 7, 2, 3, 4]
[6, 3, 2, 3, 4, 1]
[1, 4, 1, 2, 5, 2]
7
我們發現,每個點在進行深度優先搜尋時,會遇到和其他點相同的路徑,所以優化的空間。如果當前節點在之前已經搜尋過了,這時候就需要判斷需不需要繼續搜尋。兩種情況
1、當前計算最大步數值大於之前遍歷到該點的最大步數值,說明當前路徑要比之前的路徑更優,需要更新。
2、反之,不在搜尋當前路徑。
舉個例子: 如data的第一個元素的最大路徑為:32-13-12-3,最大步數為4; 當對第二個元素遍歷時,假設按照上下左右的順序,向下34-12-3為一條路徑,此時第二元素的最大步數為3,向左34-32-13-12-3是個可行路徑,而32-13-12-3之前已經搜尋過,可以利用之前的32的最大步數值,直接比較4+1 和 3,更新第二元素的最大步數為5.
注意: 要實現這一操作,需要在遍歷的過程中使用一個map來儲存已遍歷節點的最大步數值。
第二版
函式:
# check the (i,j) is in the matrix index
def isInside(row,col,i,j):
return (i in range(row)) and (j in range(col))
# update the local-max-step map
def updateMap(data,Map,row,col,i,j):
directs = [(-1,0),(1,0),(0,-1),(0,1)]
for (dx,dy) in directs:
x,y = i+dx,j+dy
if(isInside(row,col,x,y) and data[x][y] > data[i][j] and Map[x][y] < Map[i][j]+1):
Map[x][y] = Map[i][j]+1
updateMap(data, Map,row,col,x,y)
# find the max from the max step map
def maxStep(data,row,col):
Map = [[1 for j in range(col)] for i in range(row)]
[updateMap(data,Map,row,col,i,j) for i in range(row) for j in range(col)]
print('the max step map is:')
[print(Map[i]) for i in range(row)]
return max([max(i) for i in Map])
測試結果:
if __name__=='__main__':
row,col = 6,6
data = [[32, 34, 7, 33, 21, 2],
[13, 12, 3, 11, 26, 36],
[16, 30, 22, 1, 24, 14],
[20, 23, 25, 5, 19, 29],
[27, 15, 9, 17, 31, 4],
[ 6, 18, 8, 10, 35, 28]]
print(maxStep(data,row,col))
輸出:
the max step map is:
[4, 5, 2, 3, 2, 1]
[3, 2, 1, 2, 5, 6]
[4, 7, 2, 1, 4, 1]
[5, 6, 7, 2, 3, 4]
[6, 3, 2, 3, 4, 1]
[1, 4, 1, 2, 5, 2]
7
使用一個map記錄已經遍歷的元素的最大步長數可以避免一些重複的遍歷。
還能不能優化呢?是否每個元素都有必要遍歷一遍?
重最優化的角度來講,此題實質上是解決從極大值到極小值的最長路徑的問題。即最長路徑一定是從極大值到極小值的一條路徑(從山頂走到山底)。那麼我們可以先找出所有最小值或者最大值,然後使用深度優先搜尋遍歷這些點,這些路徑中,最大的一條路徑一定是整個圖中最大的路徑。這就衍生了第三個版本(從極小值往上搜,是個上山的過程)。
版本三
函式:
# check the (i,j) is in the matrix index
def isInside(row,col,i,j):
return (i in range(row)) and (j in range(col))
# check data[i][j] is the local minima
def isLocalMinima(data,row,col,i,j):
directs = [(-1,0),(1,0),(0,-1),(0,1)]
invalid_directs = [isInside(row,col,i+dx,j+dy) and data[i][j]>data[i+dx][j+dy] for (dx,dy) in directs]
return not any(invalid_directs)
# find the local minima
def findLocalMinimaElements(data,row,col):
minima = []
for i in range(row):
for j in range(col):
if isLocalMinima(data,row,col,i,j):
minima.append((i,j))
return minima
# update the local-max-step map
def updateMap(data,Map,row,col,i,j):
directs = [(-1,0),(1,0),(0,-1),(0,1)]
for (dx,dy) in directs:
x,y = i+dx,j+dy
if(isInside(row,col,x,y) and data[x][y] > data[i][j] and Map[x][y] < Map[i][j]+1):
Map[x][y] = Map[i][j]+1
updateMap(data, Map,row,col,x,y)
# main function
def maxStep(data,row,col):
minima = findLocalMinimaElements(data,row,col)
Map = [[1 for j in range(col)] for i in range(row)]
for (min_x,min_y) in minima:
updateMap(data,Map,row,col,min_x,min_y)
print('the max step map is:')
[print(Map[i]) for i in range(row)]
return max([max(i) for i in Map])
測試結果:
if __name__=='__main__':
row,col = 6,6
data = [[32, 34, 7, 33, 21, 2],
[13, 12, 3, 11, 26, 36],
[16, 30, 22, 1, 24, 14],
[20, 23, 25, 5, 19, 29],
[27, 15, 9, 17, 31, 4],
[ 6, 18, 8, 10, 35, 28]]
print(maxStep(data,row,col))
輸出:
the max step map is:
[4, 5, 2, 3, 2, 1]
[3, 2, 1, 2, 5, 6]
[4, 7, 2, 1, 4, 1]
[5, 6, 7, 2, 3, 4]
[6, 3, 2, 3, 4, 1]
[1, 4, 1, 2, 5, 2]
7
還能優化嗎?
目前還沒想到特別好的方法,只能以空間換時間。在對極小值進行深度優先搜尋時,每個極小值可以獨立使用一張圖儲存可行路徑上每個元素的最大步數值。然後使用多執行緒跑,最優比較這些圖中的最大值,即為最大步數。這個對大資料矩陣應該會有效果吧。