[Leetcode Weekly Contest]286
链接:LeetCode
[Leetcode]2215. 找出两数组的不同
给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,请你返回一个长度为 2 的列表 answer ,其中:
answer[0] 是 nums1 中所有 不 存在于 nums2 中的 不同 整数组成的列表。
answer[1] 是 nums2 中所有 不 存在于 nums1 中的 不同 整数组成的列表。
注意:列表中的整数可以按 任意 顺序返回。。
哈希表,遍历即可。
class Solution {
public List<List<Integer>> findDifference(int[] nums1, int[] nums2) {
List<List<Integer>> res = new ArrayList<>();
for(int i=0;i<2;++i) res.add(new ArrayList<Integer>());
Set<Integer> s1 = new HashSet<>();
Set<Integer> s2 = new HashSet<>();
for(var n1:nums1) s1.add(n1);
for(var n2:nums2) s2.add(n2);
for(var n1:s1) {
if(!s2.contains(n1)) res.get(0).add(n1);
}
for(var n2:s2) {
if(!s1.contains(n2)) res.get(1).add(n2);
}
return res;
}
}
[Leetcode]2216. 美化数组的最少删除数
给你一个下标从 0 开始的整数数组 nums ,如果满足下述条件,则认为数组 nums 是一个 美丽数组 :
nums.length 为偶数
对所有满足 i % 2 == 0 的下标 i ,nums[i] != nums[i + 1] 均成立
注意,空数组同样认为是美丽数组。
你可以从 nums 中删除任意数量的元素。当你删除一个元素时,被删除元素右侧的所有元素将会向左移动一个单位以填补空缺,而左侧的元素将会保持 不变 。
返回使 nums 变为美丽数组所需删除的 最少 元素数目。
遍历即可,可以设置一个标志位判断下标。
class Solution {
public int minDeletion(int[] nums) {
int res = 0;
int n = nums.length;
boolean flag = true;
for(int i=0;i<n-1;++i) {
if(flag && nums[i] == nums[i+1]) {
res ++;
}
else flag = !flag;
}
if(flag) res ++;
return res;
}
}
[Leetcode]2217. 找到指定长度的回文数
给你一个整数数组 queries 和一个 正 整数 intLength ,请你返回一个数组 answer ,其中 answer[i] 是长度为 intLength 的 正回文数 中第 queries[i] 小的数字,如果不存在这样的回文数,则为 -1 。
回文数 指的是从前往后和从后往前读一模一样的数字。回文数不能有前导 0 。
只看回文数的左半部分,可以发现左半部分是从 \(1000\cdots0\) 开始,逐渐增加的。反转这个数,拼到左半部分之后即为第 q 个长为 intLength 的回文数。
class Solution {
public long[] kthPalindrome(int[] queries, int intLength) {
int k = (intLength+1) / 2;
int maxK = (int)(9 * Math.pow(10 , k-1));
int n = queries.length;
long[] res = new long[n];
for(int i=0;i<n;++i) {
int query = queries[i];
res[i] = query > maxK ? -1 : getKthPalindrome(query, intLength, k);
}
return res;
}
public long getKthPalindrome(int query, int intLength, int k) {
char[] res = new char[intLength];
query --;
for(int i=0;i<k;++i) {
if(i == 0) res[i] = (char)(query / Math.pow(10, k-1)+'1');
if(i !=0 ) res[i] = (char)(query / Math.pow(10, k-i-1)+'0');
query = (int)(query % Math.pow(10, k-i-1));
}
int j = 0;
for(int i=intLength-1;i>=k;--i) {
res[i] = res[j];
j++;
}
long result = 0L;
for (var ch:res) {
result = result * 10 + (ch-'0');
}
return result;
}
}
[Leetcode]2218. 从栈中取出 K 个硬币的最大面值和
一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币。
每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里。
给你一个列表 piles ,其中 piles[i] 是一个整数数组,分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k ,请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少 。
朴素的,通过三维DP可以解答,但我们可以发现再每次循环时都需要对getCoinValue进行累加,这在同一个栈的循环中是浪费的,因为我们对于同一个i的栈,都需要计算其前缀。
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int n = piles.size();
int[][] dp = new int[n][k+1];
for(int i = 0;i<n;++i) {
for(int j=0;j<k+1;++j) {
if(j==0) dp[i][0] = 0;
else if(i==0) dp[0][j] = piles.get(0).subList(0, Math.min(piles.get(0).size(), j)).stream().collect(Collectors.summingInt(Integer::intValue));
else {
int getCoinValue = 0;
for(int getCoin = 0; getCoin <= Math.min(j, piles.get(i).size()); ++ getCoin) {
if(getCoin !=0 ) getCoinValue += piles.get(i).get(getCoin-1);
dp[i][j] = Math.max(dp[i][j], dp[i-1][j-getCoin] + getCoinValue);
}
}
}
}
return dp[n-1][k];
}
}
因此,我们可以先对每个栈求其前缀和 \(\textit{sum}\),\(\textit{sum}\) 的第 \(j\) 个元素视作一个体积为 \(j\),价值为 \(\textit{sum}[j]\) 的物品。
问题转化成求从 n 个物品组里面取物品体积和为 k 的物品,且每组至多取一个物品时的物品价值最大和,即分组背包模型。
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
var f = new int[k + 1];
var sumN = 0;
for (var pile : piles) {
var n = pile.size();
for (var i = 1; i < n; ++i)
pile.set(i, pile.get(i) + pile.get(i - 1)); // pile 前缀和
sumN = Math.min(sumN + n, k); // 优化:j 从前 i 个栈的大小之和开始枚举(不超过 k)
for (var j = sumN; j > 0; --j)
for (var w = 0; w < Math.min(n, j); ++w)
f[j] = Math.max(f[j], f[j - w - 1] + pile.get(w)); // w 从 0 开始,物品体积为 w+1
}
return f[k];
}
}