活跃农民
- 积分
- 509
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2013-6-5
- 最后登录
- 1970-1-1
|
0519 1. Bricks Falling When Hit
class DisjointSet {
constructor (n) {
this.parent = Array(n).fill(-1)
}
union (x, y) {
let xRoot = this.find(x)
let yRoot = this.find(y)
// !!! important
if (xRoot === yRoot) {
return
}
const xSize = this.getSize(xRoot)
const ySize = this.getSize(yRoot)
if (xSize > ySize) {
;[xRoot, yRoot] = [yRoot, xRoot]
}
// !!! tricky, order matters if you use root parent to represent size.
// you must reassign root after you calc size
this.parent[yRoot] = this.parent[yRoot] + this.parent[xRoot]
this.parent[xRoot] = yRoot
}
getSize (root) {
return -this.parent[root]
}
find (x) {
// !!!! this few lines are tricky
// and you can't simply turn it into a while loop because you need the calc'd value to set parent, that effectively require a stack for the bottom up part
if (this.parent[x] < 0) {
return x
}
this.parent[x] = this.find(this.parent[x])
return this.parent[x]
}
}
/**
* @param {number[][]} grid
* @param {number[][]} hits
* @return {number[]}
*/
var hitBricks = function(grid, hits) {
// grid and hits
// we'll copy the grid, apply the hits, get a final grid
// normalize that grid into one demension. Add a dummy root. Build a forest from that grid.
// backwards on that final one, apply the hits, only when there is a brick in the original one
// apply it, union any side it touches
// each time we calc the delta size of root
// in the end just reverse the array we have and return
if (hits == null || hits.length === 0) {
return []
}
if (grid == null || grid.length === 0 || grid[0].length === 0) {
return hits.map(i => 0)
}
const aGrid = grid.map(row => row.map(el => el))
hits.forEach(([i, j]) => aGrid[i][j] = 0)
// just use the total size of the array? lots of unused items, 0s
// or build a two-way mapping?
const rowLen = grid.length
const colLen = grid[0].length
const size = rowLen * colLen
const dSet = new DisjointSet(size + 1) // !!! + 1 because we added one more node
const getNode = (i, j) => colLen * i + j
const getIndices = node => [Math.trunc(node / colLen), node % colLen]
const top = size
// connect top
for (let j = 0; j < colLen; j++) {
if (aGrid[0][j] === 1) {
dSet.union(getNode(0, j), top)
}
}
// connect any
const moves = [[1, 0], [-1, 0], [0, 1], [0, -1]]
for (let i = 0; i < rowLen; i++) {
for (let j = 0; j < colLen; j++) {
const node = getNode(i, j)
if (aGrid[i][j] === 1) {
for (const [dx, dy] of moves) {
const x = i + dx
const y = j + dy
if (x >= 0 && x < rowLen && y >= 0 && y < colLen && aGrid[x][y] === 1) {
dSet.union(node, getNode(x, y))
}
}
}
}
}
// apply one by one backwards
const res = []
const getTopSize = () => {
const root = dSet.find(top)
return dSet.getSize(root)
}
let prevSize = getTopSize() // !!! pre is before an operation, prev is previous, different. Use the right word
hits = hits.reverse()
for (const [i, j] of hits) {
if (grid[i][j] === 1) {
aGrid[i][j] = 1
const node = getNode(i, j)
// top
if (i === 0) {
dSet.union(node, top)
}
// four directions
for (const [dx, dy] of moves) {
const [x, y] = [i + dx, j + dy]
if (x >= 0 && x < rowLen && y >= 0 && y < colLen && aGrid[x][y] === 1) {
dSet.union(node, getNode(x, y))
}
}
// get new top size and get delta
const size = getTopSize()
const delta = Math.max(0, size - prevSize - 1)
res.push(delta)
prevSize = size
} else { // !!!! don't omit the else, else you have missing points
res.push(0)
}
}
res.reverse()
return res
};
const grid = [[1,0,0,0],[1,1,1,0]]
const hits = [[1,0]]
console.log(hitBricks(grid, hits))
console.log(hitBricks([[1,0,0,0],[1,1,0,0]], [[1,1],[1,0]]))
|
|