LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)相关的知识,希望对你有一定的参考价值。
第252场周赛
又是被自己菜哭的一天
5830. 三除数
题目描述
给你一个整数 n 。如果 n 恰好有三个正除数 ,返回 true ;否则,返回 false 。
如果存在整数 k ,满足 n = k * m ,那么整数 m 就是 n 的一个 除数 。
示例 1:
输入:n = 2
输出:false
解释:2 只有两个除数:1 和 2 。
示例 2:
输入:n = 4
输出:true
解释:4 有三个除数:1、2 和 4 。
提示:
1 <= n <= 104
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/three-divisors
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
恰好有三个…就因为这个错了两次
除了1和自己本身,还有一个除数
class Solution {
public boolean isThree(int n) {
boolean flag = false;
for(int i = 2; i < n; i++){
if(!flag && n / i * i == n){
flag = true;
continue;
}
if(flag && n / i * i == n)
return false;
}
return flag;
}
}
看官解,x = n / i,如果 i 和 x 不相同,那么肯定有两个除数
如果i 和x 相同,只加一个
所以除了1和本身,就是一个平方数才满足条件
5831. 你可以工作的最大周数
题目描述
给你 n 个项目,编号从 0 到 n - 1 。同时给你一个整数数组 milestones ,其中每个 milestones[i] 表示第 i 个项目中的阶段任务数量。
你可以按下面两个规则参与项目中的工作:
每周,你将会完成 某一个 项目中的 恰好一个 阶段任务。你每周都 必须 工作。
在 连续的 两周中,你 不能 参与并完成同一个项目中的两个阶段任务。
一旦所有项目中的全部阶段任务都完成,或者仅剩余一个阶段任务都会导致你违反上面的规则,那么你将 停止工作 。注意,由于这些条件的限制,你可能无法完成所有阶段任务。
返回在不违反上面规则的情况下你 最多 能工作多少周。
示例 1:
输入:milestones = [1,2,3]
输出:6
解释:一种可能的情形是:
- 第 1 周,你参与并完成项目 0 中的一个阶段任务。
- 第 2 周,你参与并完成项目 2 中的一个阶段任务。
- 第 3 周,你参与并完成项目 1 中的一个阶段任务。
- 第 4 周,你参与并完成项目 2 中的一个阶段任务。
- 第 5 周,你参与并完成项目 1 中的一个阶段任务。
- 第 6 周,你参与并完成项目 2 中的一个阶段任务。
总周数是 6 。
示例 2:
输入:milestones = [5,2,1]
输出:7
解释:一种可能的情形是:
- 第 1 周,你参与并完成项目 0 中的一个阶段任务。
- 第 2 周,你参与并完成项目 1 中的一个阶段任务。
- 第 3 周,你参与并完成项目 0 中的一个阶段任务。
- 第 4 周,你参与并完成项目 1 中的一个阶段任务。
- 第 5 周,你参与并完成项目 0 中的一个阶段任务。
- 第 6 周,你参与并完成项目 2 中的一个阶段任务。
- 第 7 周,你参与并完成项目 0 中的一个阶段任务。
总周数是 7 。
注意,你不能在第 8 周参与完成项目 0 中的最后一个阶段任务,因为这会违反规则。
因此,项目 0 中会有一个阶段任务维持未完成状态。
提示:
n == milestones.length
1 <= n <= 105
1 <= milestones[i] <= 109
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-number-of-weeks-for-which-you-can-work
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
我明显记得做过一个两个人拿两个石头相撞,然后剩下一个石头的题
然后到这里又忘了咋做了
第一眼看到还是觉得应该就是两个相互抵消,从大到小排序以后,开始从到小抵消,然后测试用例卡住之后,开始想到底应该怎么抵消。[2,2,2]这种情况怎么处理
然后终于想到,应该是将两个最大的数相互抵消到第三个大的数减1,这样依次往后抵消,最后就能得到正确答案
但是无奈超时了,下面是当时写的代码
class Solution {
public long numberOfWeeks(int[] milestones) {
int l = milestones.length;
if(l == 1)
return 1;
PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> (b-a));
long sum = 0;
for(int i = 0; i < l; i++){
pq.offer(milestones[i]);
sum += milestones[i];
}
long count = 0;
while(pq.size() > 1){
int first = pq.poll();
int sec = pq.poll();
if(!pq.isEmpty()){
int top = pq.peek();
int cut = sec - top + 1;
first = first - cut;
sec = sec - cut;
if(first > 0)
pq.offer(first);
if(sec > 0)
pq.offer(sec);
}else{
if(Math.abs(first - sec) > 0)
pq.offer(Math.abs(first - sec));
}
}
int lev = pq.isEmpty() ? 0 : pq.poll() - 1;
return sum - (long)lev;
}
}
一结束,就去看大佬们怎么想的,结果发现是判断最大值就可以了,不过自己看着那个代码想了半天,也没想起来是怎么贪心的,唉,看答案吧
怎么理解呢?
首先对两个比较的数有个认知
就是max,也就是数组中的最大值;另一个是剩余值rest,也就是sum-max
如果max = rest + 1,那么隔一周安排一个max的任务,那么恰好可以全部安排完成
如果max > rest + 1,那么肯定会有剩余,能连续工作的周数就是2 * rest + 1
最难理解的max<rest + 1,这种情况下,为什么肯定能完成呢?
看了一圈,有两个比较好的解释,一个是插空,一个还是抵消
第一种,有max堵墙,然后其他数从大到小一次插空放在两个墙中间,是肯定可以放下的
第二种,rest中的数相互抵消,抵消到和max一样大,就可以全部被安排了。但是能抵消到max一样大么,因为rest中所有数都是比max小的,所以抵消到只有一个数时,是肯定比max小的,所以有多个数的时候,肯定能和max一样大
找不到原来做的那个题了,记得那个题有更好的解释,找到再来补充
class Solution {
public long numberOfWeeks(int[] milestones) {
int l = milestones.length;
long max = 0;
long sum = 0;
for(int i = 0; i < l; i++){
sum += milestones[i];
max = Math.max(max, milestones[i]);
}
long rest = sum - max;
if(max >= rest + 1)
return 2 * rest + 1;
else
return sum;
}
}
5187. 收集足够苹果的最小花园周长
题目描述
给你一个用无限二维网格表示的花园,每一个 整数坐标处都有一棵苹果树。整数坐标 (i, j) 处的苹果树有 |i| + |j| 个苹果。
你将会买下正中心坐标是 (0, 0) 的一块 正方形土地 ,且每条边都与两条坐标轴之一平行。
给你一个整数 neededApples ,请你返回土地的 最小周长 ,使得 至少 有 neededApples 个苹果在土地 里面或者边缘上。
|x| 的值定义为:
如果 x >= 0 ,那么值为 x
如果 x < 0 ,那么值为 -x
示例 1:
输入:neededApples = 1
输出:8
解释:边长长度为 1 的正方形不包含任何苹果。
但是边长为 2 的正方形包含 12 个苹果(如上图所示)。
周长为 2 * 4 = 8 。
示例 2:
输入:neededApples = 13
输出:16
示例 3:
输入:neededApples = 1000000000
输出:5040
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-garden-perimeter-to-collect-enough-apples
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这个题挺简单的,我当时就是找每一圈的规律,然后一圈圈加起来,注意,用long
class Solution {
public long minimumPerimeter(long neededApples) {
long count = 0;
long idx = 1;
while(count < neededApples){
long left = idx;
long right = idx + idx;
long temp = (left + right - 1) * (right - left) - left;
count += (temp + right) * 4;
idx++;
//System.out.println(count);
}
return (idx - 1) * 2 * 4;
}
}
看大佬题解时发现都是用的二分
要用二分的话,必须知道对于当前边长的正方形,苹果的数目
也就是得找到这样一个公式
设右上角顶点的坐标为(idx,idx)
将一圈分成8段,除了四个角和正方向的四个位置,每一段都是(idx,1)到(idx,idx -1),每一段的苹果数为idx *(idx - 1)+(1+idx - 1) *(idx - 1)/2,在加上四个角的8idx,正上下左右4idx
也就是说,一圈共有苹果12idx^2
这样就相当于知道了递推公式,求通项公式
已知f(n)-f(n-1)= 12n^2,f(0) = 0,求f(n)
累加法,f(n)-f(0) = 12n ^ 2+12(n-1) ^ 2 + … + 12 * 1 + 0
也就是求1到n的平方和,高中还能记着,现在肯定忘了…答案是n(n+1)(2n+1)/6
注意这里,所有变量都要用long,否则乘起来会有错误
class Solution {
public long minimumPerimeter(long neededApples) {
long left = 1;
long right = 100000;
while(left < right){
long mid = (right - left) / 2 + left;
long temp = 2 * mid * (mid + 1) * (2 * mid + 1);
if(temp >= neededApples){
right = mid;
}else{
left = mid + 1;
}
}
return 8 * left;
}
}
5833. 统计特殊子序列的数目
题目描述
特殊序列 是由 正整数 个 0 ,紧接着 正整数 个 1 ,最后 正整数 个 2 组成的序列。
比方说,[0,1,2] 和 [0,0,1,1,1,2] 是特殊序列。
相反,[2,1,0] ,[1] 和 [0,1,2,0] 就不是特殊序列。
给你一个数组 nums (仅 包含整数 0,1 和 2),请你返回 不同特殊子序列的数目 。由于答案可能很大,请你将它对 109 + 7 取余 后返回。
一个数组的 子序列 是从原数组中删除零个或者若干个元素后,剩下元素不改变顺序得到的序列。如果两个子序列的 下标集合 不同,那么这两个子序列是 不同的 。
示例 1:
输入:nums = [0,1,2,2]
输出:3
解释:特殊子序列为 [0,1,2,2],[0,1,2,2] 和 [0,1,2,2] 。
示例 2:
输入:nums = [2,2,0,0]
输出:0
解释:数组 [2,2,0,0] 中没有特殊子序列。
示例 3:
输入:nums = [0,1,2,0,1,2]
输出:7
解释:特殊子序列包括:
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 2
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-number-of-special-subsequences
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Solution {
public static final int MOD = (int)1.0e9 + 7;
public int countSpecialSubsequences(int[] nums) {
//感觉给我时间应该能做出来,感觉应该去多看看这种子序列的题了
//想想怎么搞,还是不会
//定义dp[i][j]为使用 nums[0..i] 中的元素可以组成的类型为 j 的子序列的数目
//j = 0,表示都是i的子序列
//j = 1,表示0接着1的子序列
//j = 2,表示0接着1接着2的子序列
//当nums[i] = 0时,可以接在0的子序列后面,也可以单独成为一个0子序列
//nums[i] = 1时,可以接在1的子序列后面,也可以接在0的子序列后面
//nums[i] = 2时,可以接在1的子序列后面,可以接在2的后面
int l = nums.length;
int[][] dp = new int[l][3];
dp[0][0] = nums[0] == 0 ? 1 : 0;
dp[0][1] = 0;
dp[0][2] = 0;
for(int i = 1; i < l; i++){
if(nums[i] == 0){
dp[i][0] = (2 * dp[i - 1][0] + 1) % MOD;
dp[i][1] = dp[i - 1][1];
dp[i][2] = dp[i - 1][2];
}
if(nums[i] == 1){
dp[i][1] = (2 * dp[i - 1][1] % MOD + dp[i - 1][0]) % MOD;
dp[i][0] = dp[i - 1][0];
dp[i][2] = dp[i - 1][2];
}
if(nums[i] == 2){
dp[i][2] = (2 * dp[i - 1][2] % MOD + dp[i - 1][1]) % MOD;
dp[i][1] = dp[i - 1][1];
dp[i][0] = dp[i - 1][0];
}
}
return dp[l - 1][2];
}
}
以上是关于LeetCode 第252场周赛(贪心,平方和公式,子序列动态规划)的主要内容,如果未能解决你的问题,请参考以下文章