2021.8.11 每日一题
给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。
如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该序列为等差序列。
例如,[1, 3, 5, 7, 9]、[7, 7, 7, 7] 和 [3, -1, -5, -9] 都是等差序列。
再例如,[1, 1, 2, 5, 7] 不是等差序列。
数组中的子序列是从数组中删除一些元素(也可能不删除)得到的一个序列。
例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。
题目数据保证答案是一个 32-bit 整数。
示例 1:
输入:nums = [2,4,6,8,10]
输出:7
解释:所有的等差子序列为:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]
示例 2:
输入:nums = [7,7,7,7,7]
输出:16
解释:数组中的任意子序列都是等差子序列。
提示:
1 <= nums.length <= 1000
-2^31 <= nums[i] <= 2^31 - 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
动规:很容易想到要记录每个位置的每个公差对应的序列长度
但是如何开始这个动规是个问题,看题解后发现是把当前数和之前所有数的差都记录下来
注意数的范围,做差可能会越界,所以要转换成long来处理
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
//想想怎么动规
//其实我有个思路,就是用哈希表记录每个数字出现的位置
//然后遍历,记录每个位置,以它为结尾,并且公差是t的等差数列的长度;如果后面还有跟他公差相同的,那么就把这个序列加1
//其实往往就差临门一脚
//我没有想好这个动规该怎么开始,所以去看了题解,weiwei哥写的太好了
//发现是记录和前面每个数的差值,并且记录这个差值下出现的元素个数
//然后这个差值和元素个数是用一个哈希表数组存放的
int l = nums.length;
//哈希数组,存放差值和对应个数
Map<Long, Integer>[] map = new HashMap[l];
for(int i = 0; i < l; i++){
map[i] = new HashMap<>();
}
int res = 0;
//动规
for(int i = 1; i < l; i++){
Map<Long, Integer> curr = map[i];
for(int j = 0; j < i; j++){
//两个数的差值
long diff = (long)nums[i] - (long)nums[j];
Map<Long, Integer> temp = map[j];
//如果差值在temp中有,那么就可以把这个差值的序列加1,如果没有,那么就赋值为1
//这里有个问题,就是对于示例2来说,每个位置差都相同,那么就提示我们,不应该仅仅加之前temp中的结果
//还应该把当前已存在的结果加上
curr.put(diff, curr.getOrDefault(diff, 0) + temp.getOrDefault(diff, 0) + 1);
//对于结果的贡献,加了已当前数为结尾的几个等差序列
res += temp.getOrDefault(diff, 0);
}
}
return res;
}
}
//[0,2000000000,-294967296]错了
//为啥呢,越界了
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这应该是我刚开始做动规时候遇到的题,转眼一年快过去了
首先要想分成两个和等的子集,那么和为sum的一半
如果和是奇数,那么不可以;如果最大元素大于sum的一半,也不行
然后要找的就是数组中的元素,能否求得和为sum/2
典型的背包问题
class Solution {
public boolean canPartition(int[] nums) {
int l = nums.length;
int sum = 0;
int max = 0;
for(int num : nums){
sum += num;
max = Math.max(max, num);
}
if(sum % 2 == 1)
return false;
int target = sum / 2;
if(max > target)
return false;
boolean[][] dp = new boolean[l + 1][target + 1];
for(int i = 0; i <= l; i++){
dp[i][0] = true;
}
for(int i = 1; i <= l; i++){
for(int j = 1; j <= target; j++){
dp[i][j] = dp[i - 1][j];
if(j >= nums[i - 1])
dp[i][j] |= dp[i - 1][j - nums[i - 1]];
}
}
return dp[l][target];
}
}
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 2^31 - 1
0 <= amount <= 10^4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
完全背包问题
class Solution {
public int coinChange(int[] coins, int amount) {
//每个硬币用多次,完全背包问题
int l = coins.length;
int[] dp = new int[amount + 1];
Arrays.fill(dp, Integer.MAX_VALUE / 2);
dp[0] = 0;
for(int i = 1; i <= amount; i++){
for(int j = 0; j < l; j++){
int temp = coins[j];
if(i >= temp)
dp[i] = Math.min(dp[i], dp[i - temp] + 1);
}
}
return dp[amount] >= Integer.MAX_VALUE / 2 ? -1 : dp[amount];
}
}