中级农民
- 积分
- 109
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2015-1-14
- 最后登录
- 1970-1-1
|
6/21 https://leetcode.com/problems/dungeon-game/
1) Greedy
lz辛辛苦苦实现了最大堆(JS啥时候才可以实现堆自由?),从左上角开始使用贪心算法找到所需hp最少的路径并记录最低hp,最后如果hp为负则取绝对值再加一,如果是正则直接返回1。虽然workable但是时间复杂度惨不忍睹:
- class MaxHeap {
- constructor() {
- this.list = [];
- this.size = 0;
- }
-
- push(ele) {
- this.siftUp(ele, this.size);
- this.size++;
- }
-
- siftUp(ele, pos) {
- let parent = Math.floor((pos-1)/2);
- while(parent >=0 && this.list[parent][0] < ele[0]) {
- this.list[pos] = this.list[parent];
- pos = parent;
- parent = Math.floor((pos-1)/2);
- }
- this.list[pos] = ele;
- }
-
- pop() {
- const res = this.list[0];
- this.list[0] = this.list[--this.size];
- this.list.pop();
- if(this.size) this.siftDown(this.list[0], 0);
- return res;
- }
-
- siftDown(ele, pos) {
- let child = 2*pos+2 < this.size && this.list[2*pos+2][0] > this.list[2*pos+1][0]
- ? 2*pos+2
- : 2*pos+1;
- while(child < this.size && this.list[child][0] > ele[0]) {
- this.list[pos] = this.list[child];
- pos = child;
- child = 2*pos+2 < this.size && this.list[2*pos+2][0] > this.list[2*pos+1][0]
- ? 2*pos+2
- : 2*pos+1;
- }
- this.list[pos] = ele;
- }
- }
- /**
- * @param {number[][]} dungeon
- * [url=home.php?mod=space&uid=160137]@return[/url] {number}
- */
- var calculateMinimumHP = function(dungeon) {
- if(!dungeon || !dungeon.length) return 0;
- const mh = new MaxHeap(), HEIGHT = dungeon.length, WIDTH = dungeon[0].length;
- const flag = [...Array(HEIGHT).keys()].map(key => new Array(WIDTH));
- let min = Number.MAX_SAFE_INTEGER;
- flag[0][0] = dungeon[0][0];
- mh.push([dungeon[0][0], 0, 0]);
- while(mh.size) {
- const [hp, row, col] = mh.pop();
- min = Math.min(min, hp);
- if(row === HEIGHT-1 && col === WIDTH-1) return min < 0 ? min*-1 + 1 : 1;
- if(row < HEIGHT-1 && (!flag[row+1][col] || hp+dungeon[row+1][col] > flag[row+1][col])) {
- flag[row+1][col] = hp+dungeon[row+1][col];
- mh.push([hp+dungeon[row+1][col], row+1, col]);
- }
- if(col < WIDTH-1 && (!flag[row][col+1] || flag[row][col+1] < hp+dungeon[row][col+1])) {
- flag[row][col+1] = hp+dungeon[row][col+1];
- mh.push([hp+dungeon[row][col+1], row, col+1]);
- }
- }
- };
复制代码
T: O(m * n * log(m*n)) (由于有些cell会多次处理(从另一个角度出发需要hp更小时)有可能会更差,但大致是这个scale
S: O(m * n) (flag matrix + max heap)
2) Dynamic Programming
从最后一格开始往回倒,求在每个位置想要到达终点所需的最小hp。除了最后一行/列需要特殊处理之外,在每个格子通过选取下方和右方的最小值作为离开当前格子需要的hp,再参照当前格子的值(如果大于离开时需要的,则进入时只需要最少hp 1,如果小于离开时需要的,则需要补上差值):
- var calculateMinimumHP = function(dungeon) {
- if(!dungeon || !dungeon.length) return 0;
- const HEIGHT = dungeon.length, WIDTH = dungeon[0].length;
- const hp = new Array(WIDTH);
- for(let i=HEIGHT-1; i>=0; i--) {
- for(let j=WIDTH-1; j>=0; j--) {
- let bottom = i === HEIGHT-1 ? Number.MAX_SAFE_INTEGER : hp[j];
- let right = j === WIDTH-1 ? Number.MAX_SAFE_INTEGER : hp[j+1];
- let next = Math.min(bottom, right) === Number.MAX_SAFE_INTEGER ? 1 : Math.min(bottom, right);
- hp[j] = Math.max(next - dungeon[i][j], 1);
- }
- }
- return hp[0];
- };
复制代码
T: O(m * n)
S: O(m) (optimized with the circular queue)
相比之下不管是代码还是复杂度都简化了不要太多,但是真的不太能想通为什么一定要倒着往回算,而用同样的思路从起点开始的话就是各种坑,lz尝试调了几次最终还是放弃了。遇到这种题除了死记硬背之外还有别的可以找到最佳解法的切入点吗? |
|