题目:
给你一个大小为 m x n 的整数矩阵 grid 和一个大小为 k 的数组 queries 。
找出一个大小为 k 的数组 answer ,且满足对于每个整数 queres[i] ,你从矩阵 左上角 单元格开始,重复以下过程:
- 如果
queries[i]严格 大于你当前所处位置单元格,如果该单元格是第一次访问,则获得 1 分,并且你可以移动到所有4个方向(上、下、左、右)上任一 相邻 单元格。 - 否则,你不能获得任何分,并且结束这一过程。
在过程结束后,answer[i] 是你可以获得的最大分数。注意,对于每个查询,你可以访问同一个单元格 多次 。
返回结果数组 answer 。
算法:
方法一:并查集
说明一下运行过程,以默认输入: grid = [[1,2,3],[2,5,7],[3,5,1]], queries = [5,6,2]为例
排序后的queries->queriesArr[2,5,6]和grid排序后的arr=[1,1,2,2,3,3,5,5,7],queriesArr和arr双指针。
- step 1,第一次查询,query=2,arr=[0,1]所以下图中grid编号为0和8的节点,通过并查集自己联通自己:
和grid[0][0]联通的节点数为1个 - step 2,第二次查询从query=5,arr遍历了值[2,2,3,3],grid编号1,2,3,6的节点和step 1中联通的节点联通:
和grid[0][0]联通的节点数为5个
- step 2,第二次查询从query=6,arr遍历了值[5,5],grid编号4,7的节点和step 2中联通的节点联通:
和grid[0][0]联通的节点数为8个。
注意:
1.对grid一维化,在step 1将 grid[0][0]和grid[2][2],通过并查集分别连接到自己,对结果没有影响的,等于grid[2][2]形成了一个孤岛。如果这个孤岛可以和grid[0][0]联通,那就说明能访问到这个孤岛,合并size和uniondfind
2.在遍历完所有的grid节点之后,不同节点的father可能不是同一个,而是存在孙->父->爷的关系(标记的节点是联通的,从子节点到爷爷结点之间存在父亲节点):
3.双指针+排序,充分利用了上一次的计算结果
var unionfind []int
var size []int
func maxPoints(grid [][]int, queries []int) []int {
ans := make([]int, len(queries))
row, col := len(grid), len(grid[0])
mn := row * col
// 初始化并查集和连通块size
unionfind = make([]int, mn)
size = make([]int, mn)
for i := range unionfind {
unionfind[i] = i
size[i] = 1
}
dicts := [][]int{[]int{-1,0},[]int{1,0},[]int{0,-1},[]int{0,1}}
// 对grid展开,排序
arr := make([][]int, 0)
for i := range grid {
for j, val := range grid[i] {
arr = append(arr, []int{i, j, val })
}
}
sort.Slice(arr, func(i, j int) bool {return arr[i][2] < arr[j][2]})
// 对queries排序
queriesArr := make([]int, len(queries))
for i := range queriesArr {
queriesArr[i] = i
}
sort.Slice(queriesArr, func(i, j int) bool {return queries[queriesArr[i]] < queries[queriesArr[j]]})
j := 0
for _, i := range queriesArr {
queryVal := queries[i]
for ;j < len(arr) && arr[j][2] < queryVal; j ++ {
x, y := arr[j][0], arr[j][1]
for _, dict := range dicts {
x2, y2 := x + dict[0], y + dict[1]
if 0 <= x2 && x2 < row && 0 <= y2 && y2 < col && grid[x2][y2] < queryVal {
merge(x * col + y, x2 * col + y2)
}
}
}
if grid[0][0] < queryVal {
ans[i] = size[find(0)]
}
// fmt.Println(queryVal, unionfind, size)
}
return ans
}
func find(x int) int {
if unionfind[x] == x {
return x
}
unionfind[x] = find(unionfind[x])
return unionfind[x]
}
func merge(from int, to int) {
fromFather := find(from)
toFather := find(to)
if fromFather != toFather {
// fmt.Println("merge", from, to, fromFather,toFather)
unionfind[fromFather] = toFather
size[toFather] = size[toFather] + size[fromFather]
}
}
方法二:最小堆
方法一对grid一维化,为了得到grid的有序数组,想到有序,这里也可以用最小堆
var dicts = [][]int{[]int{-1,0},[]int{1,0},[]int{0,-1},[]int{0,1}}
func maxPoints(grid [][]int, queries []int) []int {
ans := make([]int, len(queries))
row, col := len(grid), len(grid[0])
// 对queries排序
queriesArr := make([]int, len(queries))
for i := range queriesArr {
queriesArr[i] = i
}
sort.Slice(queriesArr, func(i, j int) bool {return queries[queriesArr[i]] < queries[queriesArr[j]]})
h := hp{{grid[0][0], 0, 0}}
grid[0][0] = 0
count := 0
for _, i := range queriesArr {
queryVal := queries[i]
// fmt.Println(queryVal)
for len(h) > 0 && h[0].val < queryVal {
count ++
p := heap.Pop(&h).(tuple)
// fmt.Println(queryVal,":", p)
for _, dict := range dicts {
x2, y2 := p.i + dict[0], p.j + dict[1]
if 0 <= x2 && x2 < row && 0 <= y2 && y2 < col && grid[x2][y2] > 0 {
// 注意这个顺序不能变。
heap.Push(&h, tuple{grid[x2][y2], x2, y2})
grid[x2][y2] = 0
}
}
}
ans[i] = count
}
return ans
}
type tuple struct{ val, i, j int }
type hp []tuple
func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool { return h[i].val < h[j].val }
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v interface{}) { *h = append(*h, v.(tuple)) }
func (h *hp) Pop() interface{} { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }