Unique Paths III

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实现了一个最简单的栈,用于实现遍历与回退操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值