https://leetcode.cn/contest/weekly-contest-359/
https://leetcode.cn/problems/check-if-a-string-is-an-acronym-of-words/
提示:
1 <= words.length <= 100
1 <= words[i].length <= 10
1 <= s.length <= 100
words[i] 和 s 由小写英文字母组成
class Solution {
public boolean isAcronym(List<String> words, String s) {
if (words.size() != s.length()) return false;
for (int i = 0; i < s.length(); ++i) {
if (words.get(i).charAt(0) != s.charAt(i)) return false;
}
return true;
}
}
https://leetcode.cn/problems/determine-the-minimum-sum-of-a-k-avoiding-array/
提示:
1 <= n, k <= 50
从小到大枚举所有正整数,只要与目前数组中的元素求和不等于 k ,就可以放入。
直到放入了 n 个元素。
class Solution {
public int minimumSum(int n, int k) {
Set<Integer> s = new HashSet<>();
int i = 1, ans = 0; // 从1开始尝试
while (s.size() < n) {
if (!s.contains(k - i)) { // 只要可以放入数组就放
s.add(i);
ans += i;
}
i++;
}
return ans;
}
}
https://leetcode.cn/problems/determine-the-minimum-sum-of-a-k-avoiding-array/solutions/2396408/o1-gong-shi-pythonjavacgo-by-endlesschen-cztk/
换成一句话就是说—— 0 ~ k 之间可以选最多 k / 2 个,缺少的从 k + 1 开始选,一定不会有冲突(因为大于 k 的数字和任意正整数之和不可能为 k)。
class Solution {
public int minimumSum(int n, int k) {
int m = Math.min(k / 2, n);
return (m * (m + 1) + (k * 2 + n - m - 1) * (n - m)) / 2;
}
}
https://leetcode.cn/problems/maximize-the-profit-as-the-salesman/
提示:
1 <= n <= 10^5
1 <= offers.length <= 10^5
offers[i].length == 3
0 <= starti <= endi <= n - 1
1 <= goldi <= 10^3
定义 f[i+1] 表示销售编号不超过 i 的房屋的最大盈利。
dp 数组的范围是 n,即房屋数量而不是买家数量(注意这和下面的相似题目的解法是不同的,原因是数据范围的不同)。
class Solution {
public int maximizeTheProfit(int n, List<List<Integer>> offers) {
Collections.sort(offers, (x, y) -> {
return x.get(1) - y.get(1);
});
int[] dp = new int[n + 1];
int j = 0, m = offers.size();
for (int i = 0; i <= n; ++i) {
dp[i] = i == 0? 0: dp[i - 1]; // 至少和上一个一样
while (j < m && offers.get(j).get(1) == i) {
dp[i] = Math.max(dp[i], offers.get(j).get(2) + (offers.get(j).get(0) == 0? 0: dp[offers.get(j).get(0) - 1]));
++j;
}
}
return dp[n];
}
}
https://leetcode.cn/problems/maximum-earnings-from-taxi/
提示:
1 <= n <= 10^5
1 <= rides.length <= 3 * 10^4
rides[i].length == 3
1 <= starti < endi <= n
1 <= tipi <= 10^5
注意每次赚的钱是 rides[j][1] - rides[j][0] + rides[j][2]
class Solution {
public long maxTaxiEarnings(int n, int[][] rides) {
long[] dp = new long[n + 1];
// 按照到达目的地的位置排序
Arrays.sort(rides, (a, b) -> a[1] - b[1]);
for (int i = 1, j = 0; i <= n; ++i) {
dp[i] = dp[i - 1];
while (j < rides.length && rides[j][1] == i) {
dp[i] = Math.max(dp[i], dp[rides[j][0]] + (rides[j][1] - rides[j][0] + rides[j][2]));
j++;
}
}
return dp[n];
}
}
https://leetcode.cn/problems/maximum-profit-in-job-scheduling/
提示:
1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4
1 <= startTime[i] < endTime[i] <= 10^9
1 <= profit[i] <= 10^4
https://leetcode.cn/problems/maximum-profit-in-job-scheduling/solutions/1913089/dong-tai-gui-hua-er-fen-cha-zhao-you-hua-zkcg/
由于数据范围更大了,所以只能选择 n 份兼职工作的 n 作为 dp 数组的长度,与此同时要使用二分查找确定上一份可以被同时选择的工作。
dp 数组的范围设置成 n,使用二分 查找来确定上一份可以被选择的工作。
class Solution {
public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
int n = startTime.length;
int[][] p = new int[n][3];
for (int i = 0; i < n; ++i) {
p[i] = new int[]{startTime[i], endTime[i], profit[i]};
}
Arrays.sort(p, (a, b) -> a[1] - b[1]); // 按结束时间排序
int[] dp = new int[n + 1];
for (int i = 1; i <= n; ++i) {
dp[i] = Math.max(dp[i - 1], dp[bs(p, i - 1, p[i - 1][0]) + 1] + p[i - 1][2]);
}
return dp[n];
}
public int bs(int[][] p, int r, int t) {
int l = -1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (p[mid][1] <= t) l = mid;
else r = mid - 1;
}
return l;
}
}
https://leetcode.cn/problems/maximum-number-of-events-that-can-be-attended-ii/
提示:
1 <= k <= events.length
1 <= k * events.length <= 10^6
1 <= startDayi <= endDayi <= 10^9
1 <= valuei <= 10^6
class Solution {
public int maxValue(int[][] events, int k) {
Arrays.sort(events, (a, b) -> a[1] - b[1]); // 按照结束时间排序
int n = events.length;
int[][] f = new int[n + 1][k + 1];
for (int i = 0; i < n; ++i) {
int p = search(events, i, events[i][0]); // 使用二分查找上一个可以参加的会议
for (var j = 1; j <= k; ++j) {
f[i + 1][j] = Math.max(f[i][j], f[p + 1][j - 1] + events[i][2]);
}
}
return f[n][k];
}
// 返回 endDay < upper 的最大下标
private int search(int[][] events, int right, int upper) {
var left = -1;
while (left + 1 < right) {
var mid = (left + right) / 2;
if (events[mid][1] < upper) left = mid;
else right = mid;
}
return left;
}
}
有点儿 k 次股票交易那味。
https://leetcode.cn/problems/two-best-non-overlapping-events/
提示:
2 <= events.length <= 10^5
events[i].length == 3
1 <= startTimei <= endTimei <= 10^9
1 <= valuei <= 10^6
看到数据范围中 startTime 和 endTime 都比较大,不能作为 dp 数组的范围。
class Solution {
public int maxTwoEvents(int[][] events) {
Arrays.sort(events, (a, b) -> {
return a[1] != b[1]? a[1] - b[1]: a[2] - b[2];
});
int n = events.length;
int[][] dp = new int[n + 1][2];
for (int i = 0; i < n; ++i) {
int j1 = bs(events, i, events[i][1]), j2 = bs(events, i, events[i][0]);
// while (j1 + 1 < i && events[j1 + 1][1] == events[i][0]) j1++;
dp[i + 1][0] = Math.max(events[i][2], dp[j1 + 1][0]);;
dp[i + 1][1] = Math.max(dp[i][1], dp[j2 + 1][0] + events[i][2]);
}
return Math.max(dp[n][1], dp[n][0]);
}
public int bs(int[][] p, int r, int u) {
int l = -1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (p[mid][1] >= u) r = mid - 1;
else l = mid;
}
return l;
}
}
https://leetcode.cn/problems/two-best-non-overlapping-events/solutions/1075386/yong-you-xian-dui-lie-wei-hu-ling-yi-ge-8ld3x/
由于按照开始时间排序,因为当前元素可以使用的,后面的元素一定也可以使用,即维护 mx。
class Solution {
public int maxTwoEvents(int[][] events) {
// 按开始时间排序
Arrays.sort(events, (a, b) -> (a[0] - b[0]));
// 按结束时间排序的堆
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> a[1] - b[1]);
int ans = 0, mx = 0;
for (int[] event: events) {
int s = event[0], e = event[1], p = event[2];
// 当前元素作为后一个,判断前面可以使用的最大值mx
while (!pq.isEmpty() && pq.peek()[1] < s) {
mx = Math.max(mx, pq.poll()[2]);
}
// 更新答案
ans = Math.max(ans, mx + p);
pq.offer(event);
}
return ans;
}
}
https://leetcode.cn/problems/find-the-longest-equal-subarray/
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= nums.length
0 <= k <= nums.length
思路就是:枚举右指针,尝试移动左指针,过程中更新答案。
class Solution {
public int longestEqualSubarray(List<Integer> nums, int k) {
Map<Integer, Integer> m = new HashMap<>(); // 记录子数组中每个元素的数量
int n = nums.size(), ans = 0;
for (int i = 0, j = 0; i < n; ++i) {
m.merge(nums.get(i), 1, Integer::sum); // 加入当前元素作为等值子数组的值
// 尝试移动左端点(向左向右都尝试)
while (i - j + 1 - m.get(nums.get(i)) <= k && j > 0) {
m.merge(nums.get(j - 1), 1, Integer::sum);
--j;
}
while (i - j + 1 - m.get(nums.get(i)) > k) {
m.merge(nums.get(j), -1, Integer::sum);
++j;
}
ans = Math.max(ans, m.get(nums.get(i))); // 获得当前等值子数组的长度
}
return ans;
}
}
执行用时 421ms,还是挺慢的。
https://leetcode.cn/problems/find-the-longest-equal-subarray/solutions/2396401/fen-zu-shuang-zhi-zhen-pythonjavacgo-by-lqqau/
将各个元素按照值分组之后,分别使用双指针计算以各个值作为等值子数组时的最大长度。
注意如果直接存下标的话没办法在进行双指针时快速获得应该删去的元素数量,所以存储的是 i - pos[x].size(),即存储的是每个位置之前有多少个非当前值。
class Solution {
public int longestEqualSubarray(List<Integer> nums, int k) {
int n = nums.size(), ans = 0;
List<Integer>[] pos = new ArrayList[n + 1];
Arrays.setAll(pos, e -> new ArrayList<>());
for (int i = 0; i < n; ++i) {
int x = nums.get(i);
pos[x].add(i - pos[x].size()); // 记录i-pos[x].size()而不是i
}
for (List<Integer> ls: pos) {
if (ls.size() < ans) continue;
for (int l = 0, r = 0; r < ls.size(); ++r) {
while (ls.get(r) - ls.get(l) > k) ++l;
ans = Math.max(ans, r - l + 1);
}
}
return ans;
}
}
在这篇博文中有提到过这道题目:【LeetCode周赛】2022上半年题目精选集——双指针
2271. 毯子覆盖的最多白色砖块数
提示:
1 <= tiles.length <= 5 * 10^4
tiles[i].length == 2
1 <= li <= ri <= 10^9
1 <= carpetLen <= 10^9
tiles 互相 不会重叠 。
排序 + 双指针
class Solution {
public int maximumWhiteTiles(int[][] tiles, int carpetLen) {
Arrays.sort(tiles, (a, b) -> a[0] - b[0]);
int ans = 0, cur = 0;
for (int i = 0, j = 0; j < tiles.length; ++j) {
cur += tiles[j][1] - tiles[j][0] + 1;
while (tiles[j][1] - tiles[i][1] >= carpetLen) {
cur -= tiles[i][1] - tiles[i][0] + 1;
i++;
}
ans = Math.max(ans, cur - Math.max(0, tiles[j][1] - carpetLen - tiles[i][0] + 1));
}
return ans;
}
}
脑子有点木掉了。