文章目录
竞赛链接
https://leetcode.cn/contest/biweekly-contest-118
Q1:100121. 查找包含给定字符的单词
https://leetcode.cn/problems/find-words-containing-character/description/
提示:
1 <= words.length <= 50
1 <= words[i].length <= 50
x 是一个小写英文字母。
words[i] 只包含小写英文字母。
竞赛时代码——indexOf检查
class Solution {
public List<Integer> findWordsContaining(String[] words, char x) {
List<Integer> ans = new ArrayList<>();
for (int i = 0; i < words.length; ++i) {
String word = words[i];
if (word.indexOf(x) != -1) ans.add(i);
}
return ans;
}
}
Q2:100138. 最大化网格图中正方形空洞的面积
https://leetcode.cn/problems/maximize-area-of-square-hole-in-grid/description/
提示:
1 <= n <= 10^9
1 <= m <= 10^9
1 <= hBars.length <= 100
2 <= hBars[i] <= n + 1
1 <= vBars.length <= 100
2 <= vBars[i] <= m + 1
hBars 中的值互不相同。
vBars 中的值互不相同。
竞赛时代码——贪心 排序
结果应该是长的空格和宽的空格的最小值的平方。
要先排序把相邻的放在一起。
class Solution {
public int maximizeSquareHoleArea(int n, int m, int[] hBars, int[] vBars) {
Arrays.sort(hBars);
Arrays.sort(vBars);
int hd = 1, mh = 2;
for (int i = 1; i < hBars.length; ++i) {
if (hBars[i] == hBars[i - 1] + 1) hd++;
else hd = 1;
mh = Math.max(mh, hd + 1);
}
int wd = 1, mw = 2;
for (int i = 1; i < vBars.length; ++i) {
if (vBars[i] == vBars[i - 1] + 1) wd++;
else wd = 1;
mw = Math.max(mw, wd + 1);
}
int a = Math.min(mw, mh);
return a * a;
}
}
Q3:100133. 购买水果需要的最少金币数
https://leetcode.cn/problems/minimum-number-of-coins-for-fruits/description/
提示:
1 <= prices.length <= 1000
1 <= prices[i] <= 10^5
竞赛时代码——DP
dp[i][0] 和 dp[i][1] 分别表示买到 i,不花钱买 i 和 花钱买 i 的最小花费。
class Solution {
public int minimumCoins(int[] prices) {
int n = prices.length;
int[][] dp = new int[n + 1][2];
for (int i = 1; i <= n; ++i) dp[i][0] = dp[i][1] = Integer.MAX_VALUE / 2;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
if (j + j >= i) dp[i][0] = Math.min(dp[i][0], dp[j][1]); // 可以不花钱
if (j + j >= i - 1) dp[i][1] = Math.min(dp[i][1], dp[j][1] + prices[i - 1]); // 必须花钱
}
}
return Math.min(dp[n][0], dp[n][1]);
}
}
代码优化——DP:从记忆化搜索到递推,从 O ( n 2 ) O(n^2) O(n2) 到 O ( n ) O(n) O(n) 🐂
记忆化搜索
class Solution {
int n;
int[] memo, prices;
public int minimumCoins(int[] prices) {
n = prices.length;
memo = new int[(n + 1) / 2];
this.prices = prices;
return dfs(1);
}
// dfs(i)表示获得第i个及其后面的水果所需的最少金币数
public int dfs(int i) {
if (i * 2 >= n) return prices[i - 1];
if (memo[i] != 0) return memo[i];
int res = Integer.MAX_VALUE;
// 枚举下一个购买的位置
for (int j = i + 1; j <= 2 * i + 1; ++j) {
res = Math.min(res, dfs(j));
}
return memo[i] = res + prices[i - 1];
}
}
翻译成递推
class Solution {
public int minimumCoins(int[] prices) {
int n = prices.length;
for (int i = (n + 1) / 2 - 1; i > 0; --i) {
int mn = Integer.MAX_VALUE;
for (int j = i; j <= 2 * i; ++j) {
mn = Math.min(mn, prices[j]);
}
prices[i - 1] += mn;
}
return prices[0];
}
}
单调队列优化DP
维护一个单调队列代替对 j 的循环,队列单调递减,队首在左边,队尾在右边。
class Solution {
public int minimumCoins(int[] prices) {
int n = prices.length;
// 单调递减的单调队列,队尾是最小值
Deque<int[]> dq = new ArrayDeque<>();
dq.offerLast(new int[]{n + 1, 0}); // 哨兵,方便后面的代码统一
for (int i = n; i > 0; --i) {
// 检查右端点超过窗口范围,离开窗口
while (dq.peekLast()[0] > 2 * i + 1) {
dq.pollLast();
}
int f = prices[i - 1] + dq.peekLast()[1];
// 将更大的值移除单调队列
while (f <= dq.peekFirst()[1]) {
dq.pollFirst();
}
dq.offerFirst(new int[]{i, f});
}
return dq.peekFirst()[1]; // 返回位置1
}
}
Q4:100135. 找到最大非递减数组的长度⭐
https://leetcode.cn/problems/find-maximum-non-decreasing-array-length/description/
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
解法1——单调队列优化 DP
从队首到队尾的 j 和 s[j]+last[j] 都是严格递增的。
下面是两种数组模拟单调队列的写法。
class Solution {
public int findMaximumLength(int[] nums) {
int n = nums.length;
long[] s = new long[n + 1];
int[] dp = new int[n + 1];
long[] last = new long[n + 1];
// 数组模拟单调队列,是为了方便取队列中任意位置的元素
int[] q = new int[n + 1];
int front = 1, rear = 1; // 队列中元素的范围是front~rear-1
for (int i = 1; i <= n; ++i) {
s[i] = s[i - 1] + nums[i - 1]; // 前缀和
// 1. 去掉队首无用数据(计算转移时,直接取队首)
while (front < rear && s[q[front]] + last[q[front]] <= s[i]) front++;
// 2. 计算转移
dp[i] = dp[q[front - 1]] + 1;
last[i] = s[i] - s[q[front - 1]];
// 3. 去掉队尾无用数据
while (rear > front && s[q[rear - 1]] + last[q[rear - 1]] >= s[i] + last[i]) {
rear--;
}
q[rear++] = i;
}
return dp[n];
}
}
class Solution {
public int findMaximumLength(int[] nums) {
int n = nums.length;
long[] s = new long[n + 1];
int[] dp = new int[n + 1];
long[] last = new long[n + 1];
// 数组模拟单调队列,是为了方便取队列中任意位置的元素
int[] q = new int[n + 1];
int front = 0, rear = 0; // 队列中元素的范围是front+1~rear
for (int i = 1; i <= n; ++i) {
s[i] = s[i - 1] + nums[i - 1]; // 前缀和
// 1. 去掉队首无用数据(计算转移时,直接取队首)
while (front < rear && s[q[front + 1]] + last[q[front + 1]] <= s[i]) front++;
// 2. 计算转移(最后一个被移出去的是有效的)
dp[i] = dp[q[front]] + 1;
last[i] = s[i] - s[q[front]];
// 3. 去掉队尾无用数据
while (rear > front && s[q[rear]] + last[q[rear]] >= s[i] + last[i]) {
rear--;
}
q[++rear] = i;
}
return dp[n];
}
}
当然也可以用 Deque 来做
class Solution {
public int findMaximumLength(int[] nums) {
int n = nums.length;
long[] s = new long[n + 1];
int[] dp = new int[n + 1];
long[] last = new long[n + 1];
Deque<Integer> dq = new ArrayDeque<>();
int j = 0;
for (int i = 1; i <= n; ++i) {
s[i] = s[i - 1] + nums[i - 1]; // 前缀和
// 1. 去掉队首无用数据(计算转移时,直接取队首)
while (!dq.isEmpty() && s[dq.peekFirst()] + last[dq.peekFirst()] <= s[i]) j = dq.pollFirst();
// 2. 计算转移(最后一个被移出去的是有效的)
dp[i] = dp[j] + 1;
last[i] = s[i] - s[j];
// 3. 去掉队尾无用数据
while (!dq.isEmpty() && s[dq.peekLast()] + last[dq.peekLast()] >= s[i] + last[i]) {
dq.pollLast();
}
dq.offerLast(i);
}
return dp[n];
}
}
成绩记录
T4 不会做!
前面的做的也不好!
我是~xx!