1590. 使数组和能被 P 整除
2023.3.10 每日一题
题目描述
给你一个正整数数组 nums,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
子数组 定义为原数组中连续的一组元素。
示例 1:
输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。
示例 2:
输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。
示例 3:
输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。
示例 4:
输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。
示例 5:
输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
1 <= p <= 10^9
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/make-sum-divisible-by-p
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
数据范围很大,想到会超时,但是还是写了个相对暴力的解法
加了个限制条件j < i + minlen 好像很多的,最后虽然通过了全部例子,但是还是超时了
class Solution {
public int minSubarray(int[] nums, int p) {
//搞个前缀和,然后遍历所有子数组应该就行了吧
int l = nums.length;
int[] presum = new int[l + 1];
for(int i = 1; i <= l; i++){
presum[i] = (presum[i - 1] + nums[i - 1] % p) % p;
}
if(presum[l] % p == 0)
return 0;
int minlen = l;
for(int i = 0; i < l; i++){
for(int j = i + 1; j < i + minlen && j <= l; j++){
if((presum[l] - (presum[j] - presum[i])) % p == 0){
minlen = Math.min(minlen, j - i);
break;
}
}
}
return minlen == l? -1 : minlen;
}
}
设总和为s,那么要被p整除,就要减去s - np,就是要找到一个子数组,使得子数组之和等于s - np
问题在于n不确定是多少,所以无法通过滑动窗口的方式来做
var是Java10版本中新出的特性,用它来定义局部变量。使用var定义变量的语法:var 变量名 = 初始值;1 var 不是关键字,相当于一种动态类型,编译器根据变量所赋的值来推断类型。
class Solution {
public int minSubarray(int[] nums, int p) {
//搞个前缀和,然后遍历所有子数组应该就行了吧
int l = nums.length;
int[] presum = new int[l + 1];
for(int i = 1; i <= l; i++){
presum[i] = (presum[i - 1] + nums[i - 1] % p) % p;
}
if(presum[l] == 0)
return 0;
var x = presum[l];
Map<Integer, Integer> map = new HashMap<>();
// 这是一个什么道理呢,假设数组总和%p = x
// 我们要找的是(nums[r] - nums[l]) % p = x
// 移项以后就可以转换为 (nums[r] - x + p) % p = nums[l] % p,加p是为了防止出现负数
// 然后遍历数组,将所有nums[l] % p 存储在哈希表中
// 当发现满足上面式子的r存在时,就更新minlen
int minlen = l;
for(int i = 0; i <= l; i++){
map.put(presum[i], i);
int idx = map.getOrDefault((presum[i] - x + p) % p, -l);
minlen = Math.min(minlen, i - idx);
}
return minlen == l? -1 : minlen;
}
}
class Solution:
def minSubarray(self, nums: List[int], p: int) -> int:
# python中取模运算保证了结果非负
x = sum(nums) % p
if x == 0:
return 0
l = len(nums)
minlen = l
s = 0 # 存储前缀和
mmap = {s : -1}
for i in range(l):
s = (s + nums[i]) % p
mmap[s] = i
left = mmap.get((s - x) % p, -l)
minlen = min(minlen, i - left)
return minlen if minlen != l else -1
2383. 赢得比赛需要的最少训练时长
2023.3.13 每日一题
题目描述
你正在参加一场比赛,给你两个 正 整数 initialEnergy 和 initialExperience 分别表示你的初始精力和初始经验。
另给你两个下标从 0 开始的整数数组 energy 和 experience,长度均为 n 。
你将会 依次 对上 n 个对手。第 i 个对手的精力和经验分别用 energy[i] 和 experience[i] 表示。当你对上对手时,需要在经验和精力上都 严格 超过对手才能击败他们,然后在可能的情况下继续对上下一个对手。
击败第 i 个对手会使你的经验 增加 experience[i],但会将你的精力 减少 energy[i] 。
在开始比赛前,你可以训练几个小时。每训练一个小时,你可以选择将增加经验增加 1 或者 将精力增加 1 。
返回击败全部 n 个对手需要训练的 最少 小时数目。
示例 1:
输入:initialEnergy = 5, initialExperience = 3, energy = [1,4,3,2], experience = [2,6,3,1]
输出:8
解释:在 6 小时训练后,你可以将精力提高到 11 ,并且再训练 2 个小时将经验提高到 5 。
按以下顺序与对手比赛:
- 你的精力与经验都超过第 0 个对手,所以获胜。
精力变为:11 - 1 = 10 ,经验变为:5 + 2 = 7 。
- 你的精力与经验都超过第 1 个对手,所以获胜。
精力变为:10 - 4 = 6 ,经验变为:7 + 6 = 13 。
- 你的精力与经验都超过第 2 个对手,所以获胜。
精力变为:6 - 3 = 3 ,经验变为:13 + 3 = 16 。
- 你的精力与经验都超过第 3 个对手,所以获胜。
精力变为:3 - 2 = 1 ,经验变为:16 + 1 = 17 。
在比赛前进行了 8 小时训练,所以返回 8 。
可以证明不存在更小的答案。
示例 2:
输入:initialEnergy = 2, initialExperience = 4, energy = [1], experience = [3]
输出:0
解释:你不需要额外的精力和经验就可以赢得比赛,所以返回 0 。
提示:
n == energy.length == experience.length
1 <= n <= 100
1 <= initialEnergy, initialExperience, energy[i], experience[i] <= 100
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-hours-of-training-to-win-a-competition
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Solution:
def minNumberOfHours(self, initialEnergy: int, initialExperience: int, energy: List[int], experience: List[int]) -> int:
tmp = sum(energy) + 1 - initialEnergy
res1 = tmp if tmp > 0 else 0
mmax = 0
sumx = 0
for i, x in enumerate(experience):
mmax = max(mmax, x + 1 - sumx - initialExperience)
sumx += x
return res1 + mmax
1605. 给定行和列的和求可行矩阵
2023.3.14 每日一题
题目描述
给你两个非负整数数组 rowSum 和 colSum ,其中 rowSum[i] 是二维矩阵中第 i 行元素的和, colSum[j] 是第 j 列元素的和。换言之你不知道矩阵里的每个元素,但是你知道每一行和每一列的和。
请找到大小为 rowSum.length x colSum.length 的任意 非负整数 矩阵,且该矩阵满足 rowSum 和 colSum 的要求。
请你返回任意一个满足题目要求的二维矩阵,题目保证存在 至少一个 可行矩阵。
示例 1:
输入:rowSum = [3,8], colSum = [4,7]
输出:[[3,0],
[1,7]]
解释:
第 0 行:3 + 0 = 3 == rowSum[0]
第 1 行:1 + 7 = 8 == rowSum[1]
第 0 列:3 + 1 = 4 == colSum[0]
第 1 列:0 + 7 = 7 == colSum[1]
行和列的和都满足题目要求,且所有矩阵元素都是非负的。
另一个可行的矩阵为:[[1,2],
[3,5]]
示例 2:
输入:rowSum = [5,7,10], colSum = [8,6,8]
输出:[[0,5,0],
[6,1,0],
[2,0,8]]
示例 3:
输入:rowSum = [14,9], colSum = [6,9,8]
输出:[[0,9,5],
[6,0,3]]
示例 4:
输入:rowSum = [1,0], colSum = [1]
输出:[[1],
[0]]
示例 5:
输入:rowSum = [0], colSum = [0]
输出:[[0]]
提示:
1 <= rowSum.length, colSum.length <= 500
0 <= rowSum[i], colSum[i] <= 10^8
sum(rowSum) == sum(colSum)
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-valid-matrix-given-row-and-column-sums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
没想出来贪心,想到解方程组去了哈哈哈
class Solution:
def restoreMatrix(self, rowSum: List[int], colSum: List[int]) -> List[List[int]]:
# 相当于解方程组啊,但是怎么做呢,好像只能一个个试着写
m = len(rowSum)
n = len(colSum)
matrix = [[0] * n for _ in range(m)]
# print(matrix)
# 题目思路是这样的,对于每一个格子,取当前格子能取到的最大值
# 那么遍历这个矩阵,可以构造出每一行都是满足条件的
# 最后会有一个满足条件的菊展
# 确实挺难想的
for i in range(m):
for j in range(n):
if rowSum[i] >= colSum[j]:
x = colSum[j]
matrix[i][j] = x
rowSum[i] -= x
colSum[j] -= x
else:
x = rowSum[i]
matrix[i][j] = x
rowSum[i] -= x
colSum[j] -= x
return matrix
但是写这个代码还是出了几个问题
首先创建二维数组,我写的是[[0] * n] * m,创建出来确实是m*n的矩阵,但是修改就会出问题
对某一行修改,会复制给其他行
下面这种形式没有问题