0. 赛后总结
上周日的比赛因为国庆前的加班就错过了,然后现在临着放假了,就久违地回到国际版的leetcode做了一下周赛208的virtual contest,结果4道题一小时搞定,虚拟排名国际224名。。。
呵呵,我就说leetcode-cn和我相性不合!!!(╬▔皿▔)凸
嘛,算了,口high一下而已,毕竟临场发挥也算是实力的一环吧。。。
1. 题目一
给出题目一的试题链接如下:
1. 解题思路
这一题思路还是很明确的,因为不需要考虑路径的实际存在与否,也不需要考虑最终的目录所在,只要考虑最终的路径深度就行了,因为之后就只需要返回上一级目录直到回到根目录即可。
因此,我们只需要判断每一次操作后的路径深度就行,当进入子目录时,路径深度加一,返回上一级目录时路径深度减一,反之不变。
唯一需要注意的是,当当前路径恰好为根目录时,返回上一级目录命令并不会发生作用,因为不存在更上级目录。
2. 代码实现
给出最终的代码实现如下:
class Solution:
def minOperations(self, logs: List[str]) -> int:
ans = 0
for op in logs:
if op == "./":
continue
elif op == "../":
ans = ans-1 if ans != 0 else 0
else:
ans += 1
return ans
提交代码评测得到:耗时48ms,占用内存14.5MB。
当前最优算法耗时28ms,但是看了一下其代码实现,和我们的算法思路是完全一致的,因此就不再过多展开了。
2. 题目二
给出题目二的试题链接如下:
1. 解题思路
这一题的题目描述比较长,说的比较复杂,但是解题思路却是比较清楚的,只需要按照题目意思不停地上下人即可。
当等待队列没有为空时,就考察如果在当前位置上终止运行时能够获取地最终受益,然后在所有的结果当中选取最大收益所处的时间即可。
2. 代码实现
给出python代码实现如下:
class Solution:
def minOperationsMaxProfit(self, customers: List[int], boardingCost: int, runningCost: int) -> int:
profit = 0
waiting = 0
time = -1
n = len(customers)
for idx in range(n):
c = customers[idx]
waiting += c
if waiting > 4:
nxt = profit + 4*boardingCost-runningCost
time = idx+1 if nxt > profit else time
profit = nxt
waiting -= 4
else:
nxt = profit + waiting*boardingCost-runningCost
time = idx+1 if nxt > profit else time
profit = nxt
waiting = 0
# print(idx, waiting, profit)
while waiting != 0:
if waiting > 4:
nxt = profit + 4*boardingCost-runningCost
time = n+1 if nxt > profit else time
profit = nxt
waiting -= 4
else:
nxt = profit + waiting*boardingCost-runningCost
time = n+1 if nxt > profit else time
profit = nxt
waiting = 0
break
n += 1
return time
提交代码评测得到:耗时1752ms,占用内存17.6MB。
当前的最优方案耗时820ms,但是没看懂他们的思路,然后直接试了以下他们的解法,发现是错的。。。错的。。。的。。。
累感不爱。。。>﹏<
如果有兴趣的读者可以自行研究一下,感觉应该还是有提升空间的,但是有多大就不好说了。。。
3. 题目三
给出题目三的试题链接如下:
1. 解题思路
这一题的也是题目复杂的不行。它事实上考察的就是这个继承关系的数据结构要怎么构建。而有关这个继承关系的描述,事实上可以概括为:
- 子嗣优先于兄弟,且按照年龄排序;
- 当没有子嗣时,继承人为自己的兄弟;
- 当自己连兄弟也没有时,就返回父辈的兄弟;
因此,我们可以比较轻松地看出,这是一个树形结构,我们构建继承关系时,事实上就是一个中序遍历的过程。
但是,需要注意的是,针对这个树状结构,我们需要额外记录以下两个信息:
- 兄弟/孩子的出生顺序信息;
- 每一个人的存活信息;
一种可行的方式就是构建一个人的类,用类变量来记录相关信息,不过这样相对就会烦一点。所幸由于题目限定每个人都是不同名的,因此,我们可以通过两个外部字典来记录上述两个额外信息。
2. 代码实现
给出具体的python代码实现如下:
class ThroneInheritance:
def __init__(self, kingName: str):
self.king = kingName
self.status = {kingName: True}
self.children = defaultdict(list)
def birth(self, parentName: str, childName: str) -> None:
self.children[parentName].append(childName)
self.status[childName] = True
def death(self, name: str) -> None:
self.status[name] = False
def getInheritanceOrder(self) -> List[str]:
ans = []
def dfs(name):
nonlocal ans
if self.status[name]:
ans.append(name)
for name in self.children[name]:
dfs(name)
dfs(self.king)
return ans
提交代码评测得到:耗时792ms,占用内存68.9MB。
当前的最优方案耗时688ms。看了一下他们的代码实现,核心算法思路上是完全一致的,因此就不过度展开了。
4. 题目四
给出题目四的试题链接如下:
1. 解题思路
这一题坦率地说倒是没有想到什么特别巧妙的解题方法,但是,看题目说限制了请求总数小于等于16,因此,我们尝试了一下暴力迭代的方法,结果居然通过了。。。
因此,这里的解题思路就比较简单粗暴了,就是考察每一个申请在允许和拒绝两种情况下的运行调动人数,在满足调动条件的情况下令调动人数最大。
2. 代码实现
给出python代码实现如下:
import math
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
status = [0 for i in range(n)]
def dp(idx):
if idx == len(requests):
if all(x == 0 for x in status):
return 0
else:
return -math.inf
ans = dp(idx+1)
u, v = requests[idx]
status[u] -= 1
status[v] += 1
ans = max(ans, 1 + dp(idx+1))
status[u] += 1
status[v] -= 1
return ans
return dp(0)
提交代码评测得到:耗时1920ms,占用内存14.2MB。
当前最优的代码实现耗时近36ms,需要好好考察以下他们的实现。
3. 算法优化
看了一下大佬们的算法,核心的部分事实上还是采用了迭代的方式,不过,不同于我们那么暴力地针对每一个申请进行选择与不选择的二元分类,大佬们首先使用dfs对所有的请求进行了一次粗筛,找出其中能够构成环的申请组合(只有能够构成环的组合才能保证总输入输出为0),而后对这些组合进行和我们一样的迭代运算。
这里,我们就偷懒没有自行去实现一遍大佬们的算法,这里直接摘录leetcode中大佬们的代码实现如下,有兴趣的读者可以自己研读一下。
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
# 这个算法用set 和tuple用的真好
graph = defaultdict(set)
result = 0
request_index = 0
# BUILD GRAPH AND ADD ALL REUQSTS [X, X] right to result
for from_, to in requests:
if from_ == to:
result += 1
else:
graph[from_].add((request_index, to))
request_index += 1
in_progres = set()
cycles = set()
# FIND ALL CYCLES
def dfs(from_, request_path, building_path):
for request_index, to in graph[from_]:
if to in building_path:
cycles.add(tuple(sorted(request_path[building_path.index(to):] + [request_index])))
else:
dfs(to, request_path + [request_index], building_path + [to])
for i in range(n):
dfs(i, [], [i])
cycles = [set(x) for x in cycles]
# FIND SET OF CYCLES THAT USES MAXIMUM REQUESTS
def backtrack(j, current):
max_requests = len(current)
for k in range(j, len(cycles)):
joined = current | cycles[k]
if len(joined) == len(current) + len(cycles[k]):
max_requests = max(backtrack(k + 1, joined), max_requests)
return max_requests
return result + backtrack(0, set())