馬斯克向投資者承諾:投資推特交易可獲得 3 倍回報,最高 10 倍
阿新 • • 發佈:2022-05-06
定義
記憶化搜尋其實很好理解。當我們用DFS,BFS暴力搜尋的時候,有很多狀態其實是重複計算了很多次的,這時候,我們就可以用空間換取時間,將這些狀態全都裝在數組裡,當我們再次搜尋到該狀態的時候,便可以直接返回記錄的值。
例題
e.g.1 檢查是否有合法括號字串路徑 (leetcode T2267)
這道題就是萬惡之源,本來我也不怎麼想去學記憶化搜尋的,畢竟暴搜不用動腦子嘛。但是在這場周賽中某蒟蒻遺憾2題,很懊惱,便學了記憶化(暫時不想碰dp)。
這題嘛,主要難點在怎麼有效率地處理合法括號。一開始,我的想法是憨憨棧加DFS暴搜,過不了是必然的。於是,賽後就學到了一種巧妙的記錄狀態的方法,+1/-1。
接下來就是愉快的程式碼實現環節。由於Python是真的香,我這次的程式碼是py版本的。
class Solution: def hasValidPath(self, grid: List[List[str]]) -> bool: row,col=len(grid),len(grid[0]) if(row+col)%2==0 or grid[row-1][col-1]=='(' or grid[0][0]==')': return False @lru_cache(None) #將每次計算結果記錄在快取裡,相當於visit陣列 def dfs(x:int,y:int,c:int)->bool: if x==row-1 and y==col-1:return c==1 if row-x+col-y-1<c:return False c+=1 if grid[x][y]=='(' else -1 return c>=0 and (x<row-1 and dfs(x+1,y,c) or y<col-1 and dfs(x,y+1,c)) return dfs(0,0,0)
e.g.2 網格中的最短路徑 (leetcode T 1293)
這題也是一道極好的記憶化例題。前面是個DFS,這裡來一道BFS(此題DFS會T,因為DFS的繞路太多,而BFS擴散法可以有效減少繞路的情況)。
class Solution: def shortestPath(self, grid: List[List[int]], k: int) -> int: row,col=len(grid),len(grid[0]) if row==1 and col==1:return 0 k=min(k,row+col-3) q=collections.deque([(0,0,k)]) visit=set([(0,0,k)]) step=0 while q: step+=1 cur_len=len(q) for _ in range(cur_len): x,y,c=q.popleft() for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: nx,ny=x+dx,y+dy if 0<=nx<row and 0<=ny<col: if grid[nx][ny]==0 and (nx,ny,c) not in visit: if nx==row-1 and ny==col-1: return step visit.add((nx,ny,c)) q.append((nx,ny,c)) elif grid[nx][ny]==1 and c>0 and (nx,ny,c-1) not in visit: visit.add((nx,ny,c-1)) q.append((nx,ny,c-1)) return -1
e.g.3 切披薩的方案數 (leetcode T1444)
這題巧妙融合了二維字首和與記憶化搜尋,是道好題。
- 主要思路是DFS+記憶化記錄還需要分幾塊(狀態)
- 拿到題第一個想法就是二維字首和,但是後來發現用右下角字首和的話在切的時候需要倒序遍歷(寫起來有點麻煩),所以就學了個左上角字首和
class Solution:
def ways(self, pizza: List[str], k: int) -> int:
row,col=len(pizza),len(pizza[0])
pre_apple=[[0 for _ in range(col)]for _ in range(row)]
for i in range(row-1,-1,-1):
for j in range(col-1,-1,-1):
if i==row-1 and j==col-1:
pre_apple[i][j]=1 if pizza[i][j]=='A' else 0
elif i==row-1:
pre_apple[i][j]=pre_apple[i][j+1]+1 if pizza[i][j]=='A' else pre_apple[i][j+1]
elif j==col-1:
pre_apple[i][j]=pre_apple[i+1][j]+1 if pizza[i][j]=='A' else pre_apple[i+1][j]
else:
pre_apple[i][j]=pre_apple[i+1][j]+pre_apple[i][j+1]-pre_apple[i+1][j+1]
if pizza[i][j]=='A':
pre_apple[i][j]+=1
dp = [[[-1 for _ in range(k+1)] for _ in range(col) ]for _ in range(row)]
def dfs(x,y,c:int)->int:
apple=pre_apple[x][y]
if dp[x][y][c]!=-1: #經歷過這個狀態,則直接返回相應值
return dp[x][y][c]
if pre_apple[x][y]<c: #剪枝,如果接下來的蘋果不夠c人分就返回
return 0
if c==1: #剩下一塊不用切
return 1
dp[x][y][c]=0
for i in range(x+1,row):
if pre_apple[i][y]<apple: #如果切下來的裡面有蘋果
dp[x][y][c]+=dfs(i,y,c-1)
for i in range(y+1,col):
if pre_apple[x][i]<apple:
dp[x][y][c]+=dfs(x,i,c-1)
return dp[x][y][c]%(10**9+7)
return dfs(0,0,k)