LeetCode双周赛111
A题
略
B题
略
C题
题意:数组元素下标被放到3个容器中,要求第二个容器中的最小值大于第一个容器的最大值,第三个容器的最小值大于第二个元素的最大值。(容器可以为空),求最小的操作变换次数。
思路:本质上就是数组的元素要保证是单调递增的,每个元素都可以选择变和不变,那么就转变成了01背包问题,代码如下:
public int minimumOperations(List<Integer> nums) {
var dp = new int[nums.size() + 1][4];
for (int i = 1; i <= nums.size(); i++) {
for (int k = 1; k <= 3; k++) {
dp[i][k] = Integer.MAX_VALUE;
int cnt = nums.get(i - 1) == k ? 0 : 1;
for (int j = 1; j <= k; j++) {
dp[i][k] = Math.min(dp[i][k], dp[i - 1][j]);
}
dp[i][k] += cnt;
}
}
return Math.min(dp[nums.size()][1], Math.min(dp[nums.size()][2], dp[nums.size()][3]));
}
D题
题意:给定一个正整数low,high和k,求出[low, high]范围内的美丽数目,美丽数目定义为偶数数位等于奇数数位,并且能被k整数。
思路:这种题型一看就是要用数位dp,可以想到我们构造的dp数组应该是dp[len][k][diff]
,len表示长度,k表示余数,diff表示奇数数位和偶数数位的差和。
我们使用递归的做法求出dp数组。
dfs(len, val, isLimit, isNum, s, dp, k, diff)
len代表当前遍历的长度,val代表余数,isLimit表示当前需要填的数字是否受到限制,isNum表示当前是否已经填了数字。
如果isLimit表示为true,说明后续数字会受到限制,如果
isNum为false表示当前位可以无需填写,为true表示当前位必须填写数字,并且dp可以更新。
public int numberOfBeautifulIntegers(int low, int high, int k) {
return calc(String.valueOf(high), k) - calc(String.valueOf(low - 1), k);
}
private int calc(String s, int k) {
char[] chars = s.toCharArray();
int len = chars.length;
var dp = new int[len][k][(chars.length << 1) + 1];
for (int i = 0; i < len; i++) {
for (int j = 0; j < k; j++) {
Arrays.fill(dp[i][j], -1);
}
}
int diff = chars.length;
return dfs(0, 0, true, false, chars, dp, k, diff);
}
private int dfs(int len, int val, boolean isLimit, boolean isNum, char[] s, int[][][] dp, int k, int diff) {
if (len == s.length) {
return isNum && val == 0 && diff == s.length ? 1 : 0;
}
if (!isLimit && isNum && dp[len][val][diff] != -1) {
return dp[len][val][diff];
}
int res = isNum ? 0 : dfs(len + 1, val, false, false, s, dp, k, diff);
int bit = isLimit ? s[len] - '0' : 9;
for (int j = isNum ? 0 : 1; j <= bit; j++) {
res += dfs(len + 1, (val * 10 + j) % k, isLimit && j == bit, true, s, dp, k, diff + ((j & 1) << 1) - 1);
}
if (!isLimit && isNum) {
dp[len][val][diff] = res;
}
return res;
}