Rank
Name
Score
Finish Time
Q1 (2)
Q2 (4)
Q3 (6)
Q4 (8)
Q5 (10)
171 / 2750
YoungForest
12
0:56:51
0:06:21
0:49:14
0:56:55
null
null
比赛链接
LCP 28. 采购方案
签到题。可以看到总共2750名选手签到。
二分搜索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { const int MOD = 1e9 + 7 ; public : int purchasePlans (vector<int >& nums, int target) { sort (nums.begin (), nums.end ()); int ans = 0 ; for (int i = 0 ; i < nums.size (); ++i) { const int x = nums[i]; auto it = upper_bound (nums.begin (), nums.begin () + i, target - x); ans = (ans + distance (nums.begin (), it)) % MOD; } return ans; } };
时间复杂度: O(N log N),
空间复杂度: O(1).
LCP 29. 乐团站位
一开始很简单想到模拟填数,时间复杂度为 O(N^2), 因为n 最大 10^9
. 显然已经超时了,我们需要考虑更优的解法。
观察发现,数字填写的规律十分明显。我们其实只需要计算其离起点的位置即可。通过计算目标点距离四周的距离,我们可以得到外侧总共有多少层,再通过等差数列求和,可以快速计算出外侧有多少数。然后,问题简化为目标点一定再最外圈的情况。分4种情况(上边,右边,下边,左边)分别计算目标点与左上角的距离。
虽然想出这样O(1)的解法不难,但由于过程中设计数列求和,4边情况等细节,实现起来还是有些挑战,而且一不小心还挺容易出错的。我比赛过程中也是,自己写了覆盖所有情况的test case,解决了一些bug,才一次提交通过的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { using ll = long long ; public : int helper (int n, int x, int y) { const ll left = y, right = n - 1 - y, up = x, down = n - 1 - x; const ll shell = min ({left, right, up, down}); const ll a1 = n - 1 ; const ll d = -2 ; ll shellAmount = 0 ; if (shell > 0 ) { shellAmount = 4 * (shell * a1 + shell * (shell - 1 ) * d / 2 ); } const ll startX = shell, startY = shell, len = n - 2 * shell; const ll start = (shellAmount) % 9 ; if (shell == up) { return (y - startY + start) % 9 ; } else if (shell == right) { return (x - startX + start + len - 1 ) % 9 ; } else if (shell == down) { return (len - 1 - (y - startY) + start + 2 * (len - 1 )) % 9 ; } else { return (len - 1 - (x - startX) + start + 3 * (len - 1 )) % 9 ; } } int orchestraLayout (int n, int x, int y) { return helper (n, x, y) + 1 ; } };
时间复杂度: O(1),
空间复杂度: O(1).
LCP 30. 魔塔游戏
贪心。
先遍历一遍判断和是否大于等于0,以决定最终的胜利与否。
当血量少于1时,把之前减血最多的怪物放到最后。
用一个优先队列维护怪物的减血量,方便快速找到最大的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { using ll = long long ; public : int magicTower (vector<int >& nums) { ll total = 0 ; for (int x : nums) { total += x; } if (total < 0 ) return -1 ; ll current = 1 ; priority_queue<int > pq; int ans = 0 ; for (int x : nums) { current += x; if (x < 0 ) pq.push (-x); if (current <= 0 ) { current += pq.top (); pq.pop (); ++ans; } } return ans; } };
时间复杂度: O(N log N),
空间复杂度: O(N).
LCP 31. 变换的迷宫
比赛时只想出了暴力的 DFS + DP 解法。
时间复杂度: O(times * rows * cols * 2 * rows * cols * 5) = 100 * 50 * 50 * 2 * 50 * 50 * 5 = 6,250,000,000. 果不其然,TLE了。
虽然如此,也在此给出自己TLE版本的代码。因为我认为这是一个针对类似路径问题不错的一个解法,可以针对大多数medium的题目。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution : def escapeMaze (self, maze: List [List [str ]] ) -> bool : maxT = len (maze) rows = len (maze[0 ]) cols = len (maze[0 ][0 ]) nextStep = [(1 , 0 ), (0 , 1 ), (-1 , 0 ), (0 , -1 ), (0 , 0 )] @lru_cache(None ) def dfs (t, i, j, temp, forever ): if t >= maxT: return False if i == rows - 1 and j == cols - 1 : return True maxTry = rows - 1 - i + cols - 1 - j if maxTry > maxT - t - 1 : return False for di, dj in nextStep: ni = i + di nj = j + dj if ni >= 0 and ni < rows and nj >= 0 and nj < cols and t + 1 < maxT: if maze[t + 1 ][ni][nj] == '.' : if (dfs(t + 1 , ni, nj, temp, forever)): return True else : if forever == (ni, nj): if (dfs(t + 1 , ni, nj, temp, forever)): return True else : if forever == (0 , 0 ): if (dfs(t + 1 , ni, nj, temp, (ni, nj))): return True if temp: if (dfs(t + 1 , ni, nj, False , forever)): return True return False ans = dfs(0 , 0 , 0 , True , (0 , 0 )) dfs.cache_clear() return ans;
赛后,还是要学习一下大佬们的解法。事实上,比赛中只有100名选手通过。
这个题解 是我看了所有高赞(其实也都是个位数)回答发现最有说服力,也是最好的解法。也可以辅助另外一个题解看 , 思路类似。
问题的核心在于:
如果将一个位置的陷阱 “永久” 消除,那么我们可以多次经过这一位置。但是,这等同于我们在这个位置上停留了一段时间。比如,行进的路径为 a→b→a→c→a,那么这等效于一直待在 a 不动。
因此,可以把时间复杂度中用于枚举永久消失术位置的因子去掉,也就少了50*50
,缩减到了5,000,000
.
LCP 32. 批量处理任务
真正的难题,比赛时只有31人通过。题解都少了不少。
看了不少使用高级算法做的ACM大佬,我连代码都没看懂。
找到这个贪心解法的题解 ,分享给大家。