|
| 1 | +## 675 Cut Off Trees for Golf Event |
| 2 | + |
| 3 | +#### Description |
| 4 | + |
| 5 | +[link](https://leetcode.com/problems/cut-off-trees-for-golf-event/) |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +#### General Type |
| 10 | + |
| 11 | +[A* 寻路算法](https://www.cnblogs.com/wangnfhy/p/4956711.html) |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +#### General Solution |
| 16 | + |
| 17 | +A* 算法的本质是用来寻路,上面那篇blog讲的非常详细,简单总结 |
| 18 | + |
| 19 | +发展历程:BFS -> Dijkstra -> Greedy BFS -> A* |
| 20 | + |
| 21 | +Core: |
| 22 | + |
| 23 | +- 使用两个list,其中openlist是当前需要遍历的节点,而closelist是已经遍历完成的节点 |
| 24 | +- openlist在不同情况下是不同优先队列,以F(N)为优先级 |
| 25 | +- F(N)=G(N)+H(N) |
| 26 | +- G(N)就是从起点到当前节点N的移动消耗 |
| 27 | +- H(N),在只允许上下左右移动的前提下,就是最好优先贪婪算法中当前节点N到目标节点E的曼哈顿距离 |
| 28 | + |
| 29 | +算法流程: |
| 30 | + |
| 31 | +1. 选择起始节点S和目标节点E,将(S,0)(节点,节点F(N)值)放入openList,openList是一个优先队列,节点F(N)值越小,优先级越高。 |
| 32 | +2. 判断openList是否为空,若为空,则搜索失败,目标节点不可达;否则,取出openList中优先级最高的节点P; |
| 33 | +3. 遍历P的上下左右四个相邻接点N1-N4,对每个节点N,如果N已经在closeList中,忽略;否则有两种情况, |
| 34 | + 1. 如果N不在openList中,令GN=GP+DPN,计算N到E的曼哈顿距离HN,令FN=GN+HN,令N的父节点为P,将(N,FN)放入openList; |
| 35 | + 2. 如果N已经在openList中,计算GN1= GP+DPN,如果GN1小于GN,那么用新的GN1替换GN,重新计算FN,用新的(N,FN)替换openList中旧的(N,FN),令N的父节点为P;如果GN1不小于GN,不作处理。 |
| 36 | +4. 将节点P放入closeList中。判断节点P是不是目标节点E,如果是,搜索成功,获取节点P的父节点,并递归这一过程(继续获得父节点的父节点),直至找到初始节点S,从而获得从P到S的一条路径;否则,重复步骤2; |
| 37 | + |
| 38 | +#### Solution |
| 39 | + |
| 40 | +- [Explanation](https://leetcode.com/problems/cut-off-trees-for-golf-event/discuss/107396/Python-solution-based-on-wufangjie's-(Hadlock's-algorithm)) |
| 41 | + |
| 42 | +Distance() 求解Tree到下一个Tree的距离,如果使用BFS会浪费大量时间在全图扫描上,所以这里使用一种取巧的办法 |
| 43 | + |
| 44 | +- 首先判断是否所有的tree都可以遍历到,不能的话返回-1 |
| 45 | +- 当前now数组保存所有靠近目标的节点,closer |
| 46 | +- soon数组保存所有需要绕路的节点 |
| 47 | +- 每当now遍历完而没有达到目标时,替换soon为now,此时因为soon当中都是多绕了一次路的,所以计算结果需要加上2 |
| 48 | + |
| 49 | +***为什么可以节约时间在于每次先从最优解开始求起而不是遍历全图,就算是存在绕路也接近最优解*** |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +#### Code |
| 54 | + |
| 55 | +```python |
| 56 | +class Solution: |
| 57 | + def cutOffTree(self, forest: List[List[int]]) -> int: |
| 58 | + # Add sentinels (a border of zeros) so we don't need index-checks later on. |
| 59 | + forest.append([0] * len(forest[0])) |
| 60 | + for row in forest: |
| 61 | + row.append(0) |
| 62 | + |
| 63 | + # Find the trees. |
| 64 | + trees = [(height, i, j) |
| 65 | + for i, row in enumerate(forest) |
| 66 | + for j, height in enumerate(row) |
| 67 | + if height > 1] |
| 68 | + |
| 69 | + # Can we reach every tree? If not, return -1 right away. |
| 70 | + queue = [(0, 0)] |
| 71 | + reached = set() |
| 72 | + for i, j in queue: |
| 73 | + if (i, j) not in reached and forest[i][j]: |
| 74 | + reached.add((i, j)) |
| 75 | + queue += (i+1, j), (i-1, j), (i, j+1), (i, j-1) |
| 76 | + if not all((i, j) in reached for (_, i, j) in trees): |
| 77 | + return -1 |
| 78 | + |
| 79 | + # Distance from (i, j) to (I, J). |
| 80 | + def distance(i, j, I, J): |
| 81 | + now, soon = [(i, j)], [] |
| 82 | + expanded = set() |
| 83 | + manhattan = abs(i - I) + abs(j - J) |
| 84 | + detours = 0 |
| 85 | + while True: |
| 86 | + if not now: |
| 87 | + now, soon = soon, [] |
| 88 | + detours += 1 |
| 89 | + i, j = now.pop() |
| 90 | + if (i, j) == (I, J): |
| 91 | + return manhattan + 2 * detours |
| 92 | + if (i, j) not in expanded: |
| 93 | + expanded.add((i, j)) |
| 94 | + for i, j, closer in (i+1, j, i < I), (i-1, j, i > I), (i, j+1, j < J), (i, j-1, j > J): |
| 95 | + if forest[i][j]: |
| 96 | + (now if closer else soon).append((i, j)) |
| 97 | + |
| 98 | + # Sum the distances from one tree to the next (sorted by height). |
| 99 | + trees.sort() |
| 100 | + return sum(distance(i, j, I, J) for (_, i, j), (_, I, J) in zip([(0, 0, 0)] + trees, trees)) |
| 101 | +``` |
0 commit comments