3妹:2哥2哥,你有沒有看到上海女老師出軌男學生的瓜啊。
2哥 : 看到 了,真的是太毀三觀了!
3妹:是啊, 老師本是教書育人的職業,明確規定不能和學生談戀愛啊,更何況是出軌。
2哥 : 是啊,更何況男生才16,年齡也不匹配啊。
3妹:2哥高中時有早戀嗎,2哥最早談戀愛是什么時候鴨?
2哥:切,又拿我單身狗開玩笑了。
3妹:說到最早,我今天看到一個關于“最早”的題目,讓我們一起來做下吧~
題目:
給你兩個下標從 1 開始的整數數組 nums 和 changeIndices ,數組的長度分別為 n 和 m 。
一開始,nums 中所有下標都是未標記的,你的任務是標記 nums 中 所有 下標。
從第 1 秒到第 m 秒(包括 第 m 秒),對于每一秒 s ,你可以執行以下操作 之一 :
選擇范圍 [1, n] 中的一個下標 i ,并且將 nums[i] 減少 1 。
將 nums[changeIndices[s]] 設置成任意的 非負 整數。
選擇范圍 [1, n] 中的一個下標 i , 滿足 nums[i] 等于 0, 并 標記 下標 i 。
什么也不做。
請你返回范圍 [1, m] 中的一個整數,表示最優操作下,標記 nums 中 所有 下標的 最早秒數 ,如果無法標記所有下標,返回 -1 。
示例 1:
輸入:nums = [3,2,3], changeIndices = [1,3,2,2,2,2,3]
輸出:6
解釋:這個例子中,我們總共有 7 秒。按照以下操作標記所有下標:
第 1 秒:將 nums[changeIndices[1]] 變為 0 。nums 變為 [0,2,3] 。
第 2 秒:將 nums[changeIndices[2]] 變為 0 。nums 變為 [0,2,0] 。
第 3 秒:將 nums[changeIndices[3]] 變為 0 。nums 變為 [0,0,0] 。
第 4 秒:標記下標 1 ,因為 nums[1] 等于 0 。
第 5 秒:標記下標 2 ,因為 nums[2] 等于 0 。
第 6 秒:標記下標 3 ,因為 nums[3] 等于 0 。
現在所有下標已被標記。
最早可以在第 6 秒標記所有下標。
所以答案是 6 。
示例 2:
輸入:nums = [0,0,1,2], changeIndices = [1,2,1,2,1,2,1,2]
輸出:7
解釋:這個例子中,我們總共有 8 秒。按照以下操作標記所有下標:
第 1 秒:標記下標 1 ,因為 nums[1] 等于 0 。
第 2 秒:標記下標 2 ,因為 nums[2] 等于 0 。
第 3 秒:將 nums[4] 減少 1 。nums 變為 [0,0,1,1] 。
第 4 秒:將 nums[4] 減少 1 。nums 變為 [0,0,1,0] 。
第 5 秒:將 nums[3] 減少 1 。nums 變為 [0,0,0,0] 。
第 6 秒:標記下標 3 ,因為 nums[3] 等于 0 。
第 7 秒:標記下標 4 ,因為 nums[4] 等于 0 。
現在所有下標已被標記。
最早可以在第 7 秒標記所有下標。
所以答案是 7 。
示例 3:
輸入:nums = [1,2,3], changeIndices = [1,2,3]
輸出:-1
解釋:這個例子中,無法標記所有下標,因為我們沒有足夠的秒數。
所以答案是 -1 。
提示:
1 <= n == nums.length <= 5000
0 <= nums[i] <= 10^9
1 <= m == changeIndices.length <= 5000
1 <= changeIndices[i] <= n
思路:
題意是:
你可以在任意一天完成一門課程的考試(前提是復習完成)。考試這一天不能復習。
搞定所有課程的復習+考試,至少要多少天?
提示 1
答案越大,越能夠搞定所有課程,反之越不能。
有單調性,可以二分答案。
提示 2
如果決定在第 i 天快速復習第 changeIndices[i]門課程,那么在第 i天前慢速復習這門課程是沒有意義的。同理,如果決定慢速復習某門課程,那么也沒必要對這門課程使用快速復習。
java代碼:
class Solution {public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) {int n = nums.length;int m = changeIndices.length;if (n > m) {return -1;}long slow = n; // 慢速復習+考試所需天數for (int v : nums) {slow += v;}int[] firstT = new int[n];Arrays.fill(firstT, -1);for (int t = m - 1; t >= 0; t--) {firstT[changeIndices[t] - 1] = t;}PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> a - b);int left = n - 1, right = m + 1;while (left + 1 < right) {pq.clear();int mid = (left + right) / 2;if (check(nums, changeIndices, firstT, pq, slow, mid)) {right = mid;} else {left = mid;}}return right > m ? -1 : right;}private boolean check(int[] nums, int[] changeIndices, int[] firstT, PriorityQueue<Integer> pq, long slow, int mx) {int cnt = 0;for (int t = mx - 1; t >= 0; t--) {int i = changeIndices[t] - 1;int v = nums[i];if (v <= 1 || t != firstT[i]) {cnt++; // 留給左邊,用來快速復習/考試continue;}if (cnt == 0) {if (pq.isEmpty() || v <= pq.peek()) {cnt++; // 留給左邊,用來快速復習/考試continue;}slow += pq.poll() + 1;cnt += 2; // 反悔:一天快速復習,一天考試}slow -= v + 1;cnt--; // 快速復習,然后消耗一天來考試pq.offer(v);}return cnt >= slow; // 剩余天數不能慢速復習+考試}
}