[英雄星球六月集训LeetCode解题日报] 第21日 堆
日报
- 只有第四题用了堆。
题目
一、 2285. 道路的最大总重要性
链接: 2285. 道路的最大总重要性
1. 题目描述
给你一个整数 n ,表示一个国家里的城市数目。城市编号为 0 到 n - 1 。
给你一个二维整数数组 roads ,其中 roads[i] = [ai, bi] 表示城市 ai 和 bi 之间有一条 双向 道路。
你需要给每个城市安排一个从 1 到 n 之间的整数值,且每个值只能被使用 一次 。道路的 重要性 定义为这条道路连接的两座城市数值 之和 。
请你返回在最优安排下,所有道路重要性 之和 最大 为多少。
2. 思路分析
给每个城市计数,出现次数多的给大值。
3. 代码实现
class Solution:
def maximumImportance(self, n: int, roads: List[List[int]]) -> int:
cnt = [0]*n
for a,b in roads:
cnt[a] += 1
cnt[b] += 1
cnt.sort()
return sum(k*v for k,v in enumerate(cnt,1))
二、 675. 为高尔夫比赛砍树
链接: 675. 为高尔夫比赛砍树
1. 题目描述
2. 思路分析
- 这题之前做过,用朴素BFS、优先队列BFS、A*都可以,甚至可以并查集预处理失败的情况(所有树连通)。
- 这几天刚学的双向BFS,我要写双向!
3. 代码实现
class Solution:
def cutOffTree(self, forest: List[List[int]]) -> int:
trees = [(0,(0,0))] # 从0出发
m,n = len(forest),len(forest[0])
if forest[0][0] == 0:
return -1
road = set()
for i in range(m):
for j in range(n):
if forest[i][j] > 0:
road.add((i,j))
if forest[i][j] > 1:
trees.append((forest[i][j],(i,j)))
trees.sort()
dirs = [(1,0),(-1,0),(0,1),(0,-1)]
def bfs(start,end):
if start == end:
return 0
v1 = {start:0}
v2 = {end:0}
q1 = deque(v1)
q2 = deque(v2)
def debfs(q,v1,v2):
step = v1[q[0]]+1
for _ in range(len(q)):
i,j = q.popleft()
for dx,dy in dirs:
nxt = (i+dx,j+dy)
if nxt in v2:
return step + v2[nxt]
if nxt in road and nxt not in v1:
v1[nxt] = step
q.append(nxt)
return -1
step = 0
while q1 and q2:
ret = debfs(q1,v1,v2) if len(q1)>len(q2) else debfs(q2,v2,v1)
if ret != -1:
return ret
return -1
ans = 0
for i in range(1,len(trees)):
ret = bfs(trees[i-1][1],trees[i][1])
if ret == -1:
return -1
ans += ret
return ans
三、 1405. 最长快乐字符串
链接: 1405. 最长快乐字符串
1. 题目描述
2. 思路分析
- 用一个数组记录剩余的字母数量,并且降序。
- 每次尝试拿前两种字符c1,c2用,c1用2个,c2用1个或2个,取决当前于c2的数量是否依然小于c1。
- 因为如果c2比c1少,再拿的多,后边就有c1不能找到分割字符了。
3. 代码实现
class Solution:
def longestDiverseString(self, a: int, b: int, c: int) -> str:
pool = [(a,'a'),(b,'b'),(c,'c')]
pool.sort(reverse=True)
ans = ''
while True:
cnta,a = pool[0]
cnta = min(2,cnta)
ans += a*cnta
pool[0] = (pool[0][0]-cnta,a)
cntb,b = pool[1]
if cntb == 0:
break
cntb = min(1 if cntb < pool[0][0] else 2 ,cntb)
ans += b * cntb
pool[1] = (pool[1][0]-cntb,b)
pool.sort(reverse=True)
return ans
四、 659. 分割数组为连续子序列
链接: 659. 分割数组为连续子序列
1. 题目描述
2. 思路分析
蜘蛛纸牌思路。
- 把每个数放到一个队伍里。
- 所有队伍放到一个优先队列里维护,(队尾数字,长度)。
- 因为是升序,因此新来的数一定可以追加到某个队伍队尾,不可能是队中或队首。
- 如果数字a没有队伍可以进,一定是没有看见a-1这个数。
- 开始讨论当前所有队伍情况:
- 那么如果队伍里还存在a-2,这个队就是没用的了,以后再也不存在能进来的数字,判断一下长度,移除所有这种队伍,如果长度<3,返回False。
- 然后现在堆顶的队尾一定是a或a-1,
- 队尾==a-1就追加进这个队伍;
- 队尾==a就把a单独开一个队伍。
3. 代码实现
class Solution:
def isPossible(self, nums: List[int]) -> bool:
n = len(nums)
if n < 3:
return False
q = [] # 优先队列储存每个牌队的最后一个数和长度
for a in nums:
while q and q[0][0] +1<a: # 把所有队尾元素小于a-1的队干掉,判断长度。因为后边不会有数追到它后边。
top,d = heapq.heappop(q)
if d<3:
return False
if not q: # 堆没了就加一个
q.append((a,1))
else: # 现在堆顶的队,队尾一定是a或a-1
top,d = q[0]
if top == a: # 最小队尾==这个数,不能追加把这个数放新的队
heapq.heappush(q,(a,1))
elif top + 1 == a: # 可以追加到最短队,那就追加
heapq.heapreplace(q,(a,d+1))
# print(q)
return all(d >= 3 for top,d in q)