2022-01-14 每日打卡:难题精刷
写在前面
“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。
扫雷
只要确定两个之后,后面的都可以排下去,检查是否满足最后没有剩余雷即可。枚举第一个雷的可能性。
num = input()
a = input().split(" ")
a.pop()
a = [int(x) for x in a]
ans,tmp = 0, 0
def check(fisrt, second):
for _ in range(1, len(a)):
tmp = fisrt+second
fisrt, second = second, a[_]-tmp
if not (second==0 or second==1):
break
if second==0:
return True
return False
a0 = a[0]
if a0==1:
fisrt, second = 1,0
if check(fisrt,second):
ans+=1
fisrt, second = 0, 1
if check(fisrt,second):
ans+=1
elif a0==2:
fisrt, second = 1, 1
if check(fisrt,second):
ans+=1
else:
fisrt, second =0,0
if check(fisrt, second):
ans+=1
print(ans)
积木大赛
起初想用类似拓扑的方法,每次从中筛出为0的数,后来发现了一种更为巧妙的思路,先放代码:
maxx, width, ans = 0, eval(input()), 0
a = [eval(_) for _ in input().split(" ")]
for _ in a:
if _>maxx:
ans += (_-maxx)
maxx = _
print(ans)
对于从maxx开始检查,比它高的一定至少有一次安放,所以高的只需要加上差值即可。
比它低的无须加,原因在于最开始我们指定maxx为0,比它低的一定有共同部分被加过。
304. 二维区域和检索 - 矩阵不可变
- 一维前缀和:
class NumMatrix:
def __init__(self, matrix: List[List[int]]):
m, n = len(matrix), (len(matrix[0]) if matrix else 0)
self.sums = [[0] * (n + 1) for _ in range(m)]
_sums = self.sums
for i in range(m):
for j in range(n):
_sums[i][j + 1] = _sums[i][j] + matrix[i][j]
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
_sums = self.sums
total = sum(_sums[i][col2 + 1] - _sums[i][col1] for i in range(row1, row2 + 1))
return total
- 二维前缀和:公式如下所示
- 前缀和的计算公式:
- 面积和的计算公式:
- 前缀和的计算公式:
class NumMatrix:
def __init__(self, matrix: List[List[int]]):
m, n = len(matrix), (len(matrix[0]) if matrix else 0)
self.sums = [[0] * (n + 1) for _ in range(m + 1)]
_sums = self.sums
for i in range(m):
for j in range(n):
_sums[i + 1][j + 1] = _sums[i][j + 1] + _sums[i + 1][j] - _sums[i][j] + matrix[i][j]
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
_sums = self.sums
return _sums[row2 + 1][col2 + 1] - _sums[row1][col2 + 1] - _sums[row2 + 1][col1] + _sums[row1][col1]
2132. 用邮票贴满网格图
这是力扣双周赛69场最后的困难题,当时想用枚举做,把自己枚举晕了…然后解析如下:
这个题中会用到三个矩阵:
- 二维前缀和:表示累计。用于快速检查矩阵结果,即前缀和为0时不覆盖格子。
- 二维差分:表示差值。用于快速更新矩阵加减,对每个可以放置邮票的地方都放置邮票。
- 二维计数:表示个数。用于最后的验证,遍历放置后,如果还填不满则不成立。二维计数矩阵是可以由前缀和矩阵or差分矩阵推导出来的。
class Solution:
class Solution:
def possibleToStamp(self, grid: List[List[int]], stampHeight: int, stampWidth: int) -> bool:
m, n = len(grid), len(grid[0])
sum = [[0] * (n + 1) for _ in range(m + 1)]
diff = [[0] * (n + 1) for _ in range(m + 1)]
# grid 的二维前缀矩阵
for i, row in enumerate(grid):
for j, v in enumerate(row):
sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + v
for i, row in enumerate(grid):
for j, v in enumerate(row):
if v == 0:
# 注意这是矩形右下角横纵坐标都 +1 后的位置
x, y = i + stampHeight, j + stampWidth
# 检查可以放下邮票,更新二维差分
if x <= m and y <= n and sum[x][y] - sum[x][j] - sum[i][y] + sum[i][j] == 0:
diff[i][j] += 1
diff[i][y] -= 1
diff[x][j] -= 1
diff[x][y] += 1
# 还原二维差分矩阵对应的计数矩阵,就是更新后的结果
# 这里用滚动数组实现
cnt, pre = [0] * (n + 1), [0] * (n + 1)
for i, row in enumerate(grid):
for j, v in enumerate(row):
cnt[j + 1] = cnt[j] + pre[j + 1] - pre[j] + diff[i][j]
if cnt[j + 1] == 0 and v == 0:
return False
cnt, pre = pre, cnt
return True