0x00
难度:HARD
On a 2-dimensional grid, there are 4 types of squares:
1 represents the starting square. There is exactly one starting square.
2 represents the ending square. There is exactly one ending square.
0 represents empty squares we can walk over.
-1 represents obstacles that we cannot walk over.
Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.
Example 1:
Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
Output: 2
Explanation: We have the following two paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
Example 2:
Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
Output: 4
Explanation: We have the following four paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
Example 3:
Input: [[0,1],[2,0]]
Output: 0
Explanation:
There is no path that walks over every empty square exactly once.
Note that the starting and ending square can be anywhere in the grid.
Note:
1 <= grid.length * grid[0].length <= 20
0x01
不太明白为什么这道题的难度是HARD,解题思路很简单。看了下讨论组的提交,也是同一个思路。这道题解出之后,抖音上的一笔写田直接能试试了。不说这个,说下解题思路。
思路还是很常规的DFS。
确定起点后,遍历所有可能的路径,最终走到终点。退出的条件就两个:退出点的值为2;遍历完所有的非障碍点。
0x02
type PathItem struct {
prev *PathItem
next *PathItem
n int
b uint8
}
type PathNode struct {
pItem *PathItem
}
func initItemDirs(grid [][]int, nRow, nCol int, pItem *PathItem) {
//up
if (0 == nRow) || (-1 == grid[nRow-1][nCol]) {
pItem.b |= 0x10
}
//down
if (nRow == len(grid)-1) || (-1 == grid[nRow+1][nCol]) {
pItem.b |= 0x20
}
//left
if (0 == nCol) || (-1 == grid[nRow][nCol-1]) {
pItem.b |= 0x40
}
//right
if (len(grid[0])-1 == nCol) || (-1 == grid[nRow][nCol+1]) {
pItem.b |= 0x80
}
}
func PushbackItem(pItem, pNextItem *PathItem) *PathItem {
pItem.next = pNextItem
pNextItem.prev = pItem
pNextItem.next = nil
return pNextItem
}
func PopupItem(pItem *PathItem) *PathItem {
pPrevItem := pItem.prev
pItem.next, pItem.prev = nil, nil
if pPrevItem != nil {
pPrevItem.next = nil
}
return pItem
}
func WayToNext(pNodeList []PathNode, pItem *PathItem, gridColSizes, nRow, nCol, nTotalCnt int, pnCurCnt, pnSuccessCnt *int) {
if 2 == pItem.n {
if *pnCurCnt == nTotalCnt {
*pnSuccessCnt = *pnSuccessCnt + 1
}
goto EXIT
}
//up
if (pItem.b&0x10 == 0) && (pItem.b&0x01 == 0) {
pNextNode := &pNodeList[(nRow-1)*gridColSizes+nCol]
if pNextNode.pItem != nil {
pNextItem := PushbackItem(pItem, pNextNode.pItem)
pItem.b |= 0x01
pNextNode.pItem = nil
*pnCurCnt = *pnCurCnt + 1
WayToNext(pNodeList, pNextItem, gridColSizes, nRow-1, nCol, nTotalCnt, pnCurCnt, pnSuccessCnt)
}
}
//down
if (pItem.b&0x20 == 0) && (pItem.b&0x02 == 0) {
pNextNode := &pNodeList[(nRow+1)*gridColSizes+nCol]
if pNextNode.pItem != nil {
pNextItem := PushbackItem(pItem, pNextNode.pItem)
pItem.b |= 0x02
pNextNode.pItem = nil
*pnCurCnt = *pnCurCnt + 1
WayToNext(pNodeList, pNextItem, gridColSizes, nRow+1, nCol, nTotalCnt, pnCurCnt, pnSuccessCnt)
}
}
//left
if (pItem.b&0x40 == 0) && (pItem.b&0x04 == 0) {
pNextNode := &pNodeList[nRow*gridColSizes+nCol-1]
if pNextNode.pItem != nil {
pNextItem := PushbackItem(pItem, pNextNode.pItem)
pItem.b |= 0x04
pNextNode.pItem = nil
*pnCurCnt = *pnCurCnt + 1
WayToNext(pNodeList, pNextItem, gridColSizes, nRow, nCol-1, nTotalCnt, pnCurCnt, pnSuccessCnt)
}
}
//right
if (pItem.b&0x80 == 0) && (pItem.b&0x08 == 0) {
pNextNode := &pNodeList[nRow*gridColSizes+nCol+1]
if pNextNode.pItem != nil {
pNextItem := PushbackItem(pItem, pNextNode.pItem)
pItem.b |= 0x08
pNextNode.pItem = nil
*pnCurCnt = *pnCurCnt + 1
WayToNext(pNodeList, pNextItem, gridColSizes, nRow, nCol+1, nTotalCnt, pnCurCnt, pnSuccessCnt)
}
}
EXIT:
*pnCurCnt = *pnCurCnt - 1
pItem = PopupItem(pItem)
pNodeList[nRow*gridColSizes+nCol].pItem = pItem
pItem.b &= 0xF0
}
func uniquePathsIII(grid [][]int) int {
nEleNum := len(grid) * len(grid[0])
pNodeList := make([]PathNode, nEleNum)
nStartRow := -1
nStartCol := -1
nTotalCnt := 0
for i, arr := range grid {
for j, v := range arr {
pNode := &pNodeList[i*len(grid[0])+j]
pNode.pItem = nil
if -1 == v {
continue
}
pNode.pItem = &PathItem{
prev: nil,
next: nil,
n: v,
b: 0,
}
initItemDirs(grid, i, j, pNode.pItem)
if 1 == v {
nStartRow = i
nStartCol = j
}
nTotalCnt++
}
}
nSuccessCnt := 0
nCurCnt := 1
pNode := &pNodeList[nStartRow*len(grid[0])+nStartCol]
pItem := pNode.pItem
pNode.pItem = nil
WayToNext(pNodeList, pItem, len(grid[0]), nStartRow, nStartCol, nTotalCnt, &nCurCnt, &nSuccessCnt)
return nSuccessCnt
}
每个点最多能走的方向是4个:上下左右。如果点处于库边,或者4个方向中有-1,那么这几个方向就可以不探索。这里在PathItem中设置了一个 uint8 用于记录走过的路。其中高4位记录的是当前这个点不能走分享,属于自然禀赋,每次回退不会情况;低4位属于记录走的方向,回退之后会清零。
PushbackItem 与 PopupItem实现了一个最简单的栈,用于实现遍历与回退操作。