记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
4/27 1391. 检查网格中是否存在有效路径
1 每种街道类型对应可走的方向集合。
2 从 (0, 0) 开始 BFS,尝试走到相邻格子 (nx, ny)。
3 只有当"当前格子可朝该方向走"且"相邻格子可从反方向接入"时,这一步才合法。
4 BFS 过程中若到达右下角,返回 True;遍历结束仍未到达则返回 False。
python
def hasValidPath(grid):
"""
:type grid: List[List[int]]
:rtype: bool
"""
n, m = len(grid), len(grid[0])
if n == 1 and m == 1:
return True
# 方向编码:0=上, 1=右, 2=下, 3=左
dirs = [(-1, 0), (0, 1), (1, 0), (0, -1)]
opposite = [2, 3, 0, 1]
# 每种街道可连接的方向
street = {
1: {1, 3}, # 左右
2: {0, 2}, # 上下
3: {3, 2}, # 左下
4: {1, 2}, # 右下
5: {3, 0}, # 左上
6: {1, 0}, # 右上
}
from collections import deque
q = deque([(0, 0)])
vis = {(0, 0)}
while q:
x, y = q.popleft()
if x == n - 1 and y == m - 1:
return True
t = grid[x][y]
for d in street[t]:
dx, dy = dirs[d]
nx, ny = x + dx, y + dy
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
nt = grid[nx][ny]
if opposite[d] not in street[nt]:
continue
if (nx, ny) not in vis:
vis.add((nx, ny))
q.append((nx, ny))
return False
4/28 2033. 获取单值网格的最小操作数
将所有数对 x 取模必须一致,否则不可能通过 ±x 变成同一个值
将数组排序 取中位数 计算每个数与中位数的差值 除以x 即为操作数
python
def minOperations(grid, x):
"""
:type grid: List[List[int]]
:type x: int
:rtype: int
"""
nums = [num for row in grid for num in row]
mod = nums[0] % x
for num in nums:
if num % x != mod:
return -1
nums.sort()
target = nums[len(nums) // 2]
ops = 0
for num in nums:
ops += abs(num - target) // x
return ops
4/29 3225. 网格图操作后的最大分数
把每一列最终被染黑的深度记为 d[j](取值 0...n,表示该列前 d[j] 个格子是黑色)。
一个白格能计分,当且仅当它左右有黑格。按列转移时,只和相邻列深度关系有关。
设列前缀和 prefix[j][k] 表示第 j 列前 k 个数之和,可求一段贡献。
状态压缩 DP(按列推进):
prev_pick[x]:处理到前一列时,前一列深度为 x 的最大分数(当前列会"拿"与前一列比较产生的贡献)。
prev_skip[x]:处理到前一列时,前一列深度为 x 的最大分数(当前列不拿这部分贡献,留给下一次比较)。
转移当前列深度 curr,枚举前一列深度 prev:
1 curr > prev:
说明上一列有区间 [prev, curr) 在本列更深时可形成横向相邻黑白,贡献来自第 j-1 列该段。
2 curr <= prev:
说明当前列有区间 [curr, prev) 在前一列更深时可形成贡献,来自第 j 列该段。
python
def maximumScore(grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
n = len(grid)
# prefix[j][k] = 第 j 列前 k 个元素和(k 从 0 到 n)
prefix = [[0] * (n + 1) for _ in range(n)]
for j in range(n):
for i in range(n):
prefix[j][i + 1] = prefix[j][i] + grid[i][j]
prev_pick = [0] * (n + 1)
prev_skip = [0] * (n + 1)
for j in range(1, n):
curr_pick = [0] * (n + 1)
curr_skip = [0] * (n + 1)
for curr in range(n + 1):
for prev in range(n + 1):
if curr > prev:
score = prefix[j - 1][curr] - prefix[j - 1][prev]
val = prev_skip[prev] + score
if val > curr_pick[curr]:
curr_pick[curr] = val
if val > curr_skip[curr]:
curr_skip[curr] = val
else:
score = prefix[j][prev] - prefix[j][curr]
val_pick = prev_pick[prev] + score
if val_pick > curr_pick[curr]:
curr_pick[curr] = val_pick
if prev_pick[prev] > curr_skip[curr]:
curr_skip[curr] = prev_pick[prev]
prev_pick = curr_pick
prev_skip = curr_skip
return max(prev_pick)
4/30 3742. 网格中得分最大的路径
从 (0,0) 走到 (m-1,n-1),每个格子既有得分也有花费:
0 -> 得分0 花费0
1 -> 得分1 花费1
2 -> 得分2 花费1
要求总花费 <= k 时路径得分最大。
定义 dp[i][j][c]:到达 (i,j) 且总花费恰好为 c 时的最大得分。
转移来自上方或左方,当前格子的花费是 cost(值为 0 时 cost=0,否则 cost=1):
dp[i][j][c] = max(dp[i-1][j][c-cost], dp[i][j-1][c-cost]) + grid[i][j]
起点 (0,0) 特殊:dp[0][0][0] = 0(题目保证 grid[0][0]=0)。
直接开三维数组会占用 O(mn k) 空间,容易 MLE。
由于第 i 行只依赖第 i-1 行和当前行左侧,因此可做滚动优化:
prev[j][c] 表示上一行,curr[j][c] 表示当前行,空间降为 O(nk)。
时间复杂度 O(m nk),空间复杂度 O(nk)。
python
def maxPathScore(grid, k):
"""
:type grid: List[List[int]]
:type k: int
:rtype: int
"""
m, n = len(grid), len(grid[0])
neg_inf = -10 ** 15
prev = [[neg_inf] * (k + 1) for _ in range(n)]
for i in range(m):
curr = [[neg_inf] * (k + 1) for _ in range(n)]
for j in range(n):
if i == 0 and j == 0:
curr[0][0] = 0
continue
val = grid[i][j]
cost = 1 if val > 0 else 0
up = prev[j] if i > 0 else None
left = curr[j - 1] if j > 0 else None
for c in range(cost, k + 1):
best = neg_inf
pre_c = c - cost
if up is not None and up[pre_c] > best:
best = up[pre_c]
if left is not None and left[pre_c] > best:
best = left[pre_c]
if best != neg_inf:
curr[j][c] = best + val
prev = curr
ans = max(prev[n - 1])
return -1 if ans < 0 else ans
5/1
python
5/2
python
5/3
python