LeetCode494. 目标和 / 474. 一和零 / 203. 移除链表元素 / 第 244 场力扣周赛
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode494. 目标和 / 474. 一和零 / 203. 移除链表元素 / 第 244 场力扣周赛相关的知识,希望对你有一定的参考价值。
494. 目标和
2021.6.7 每日一题
题目描述
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
动规,感觉这个月是动规月
dp[i][j]定义为到下标i目标和为j的方法数
但是这个题因为有负数,所以要将j的范围扩充一倍
设所有元素的总和为sum,因为所以提示nums[i]是大于0的,那么最大值就是sum,最小值就是-sum
将j的范围扩充一倍,使得0 ~ sum - 1表示 -sum ~ -1,sum+1 ~ 2sum表示正数
初始化的时候,第一个元素是0特殊处理
class Solution {
public int findTargetSumWays(int[] nums, int target) {
//我能想到的动规,就是dp[i][j]到下标i目标和为j的方法数
int l = nums.length;
int sum = 0;
for(int i = 0; i < l; i++){
sum += nums[i];
}
if(target > sum || target < -sum)
return 0;
//因为有负数0-sum - 1表示-sum ~ -1,sum+1-2sum表示正数
int[][] dp = new int[l][2 * sum + 1];
//如果开始是0特殊处理
if(nums[0] == 0)
dp[0][sum] = 2;
else{
dp[0][sum - nums[0]] = 1;
dp[0][sum + nums[0]] = 1;
}
for(int i = 1; i < l; i++){
for(int j = 0; j <= 2 * sum; j++){
int a = j + nums[i];
int b = j - nums[i];
if(a >= 0 && a <= 2 * sum){
dp[i][j] += dp[i - 1][a];
}
if(b >= 0 && b <= 2 * sum){
dp[i][j] += dp[i - 1][b];
}
}
}
return dp[l - 1][sum + target];
}
}
官解的数学推导:
数组元素总和为sum,在前面添加负号的元素总和为neg,其余添加+的元素总和为sum - neg
因此目标为sum- neg - neg = target
所以neg = (sum - target) / 2; (从这里可知(sum - target)是非负偶数)
然后现在的目标就是选择 x 个数,使他们的和为neg
定义dp[i][j]为表示在数组nums 的前 i 个数中选取元素,使得这些元素之和等于 j 的方案数
初始化,dp[0][0] = 1;
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int num : nums) {
sum += num;
}
int diff = sum - target;
if (diff < 0 || diff % 2 != 0) {
return 0;
}
int n = nums.length, neg = diff / 2;
int[][] dp = new int[n + 1][neg + 1];
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
int num = nums[i - 1];
for (int j = 0; j <= neg; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= num) {
dp[i][j] += dp[i - 1][j - num];
}
}
}
return dp[n][neg];
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/target-sum/solution/mu-biao-he-by-leetcode-solution-o0cp/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
474. 一和零
2021.6.6 每日一题,补2021.6.5每日一题
题目描述
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ones-and-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
看到第一眼想到的是回溯,没救了,写了一下,过了22个例子就超时了,代码没问题应该
class Solution {
int max = 0;
int m;
int n;
public int findMaxForm(String[] strs, int m, int n) {
//第一眼想到的是回溯遍历所有的情况,但是能回溯吧,应该也能动规,
//
this.m = m;
this.n = n;
int l = strs.length;
int[] count = new int[2];
backtracking(strs, 0, count, 0);
return max;
}
public void backtracking(String[] strs, int res, int[] count, int index){
if(index > strs.length)
return;
if(count[0] > m || count[1] > n)
return;
max = Math.max(max, res);
for(int i = index; i < strs.length; i++){
int count0 = 0;
for(int j = 0; j < strs[i].length(); j++){
count0 += strs[i].charAt(j) == '0' ? 1 : 0;
}
int count1 = strs[i].length() - count0;
count[0] += count0;
count[1] += count1;
backtracking(strs, res + 1, count, i + 1);
count[0] -= count0;
count[1] -= count1;
}
}
}
然后想了半天终于想出来怎么规划了,和背包问题挺像的,最近不管做什么题都老是看到动态规划,而且都还不容易想,看来又得好好看看了
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//动规动规,咋就这么难想呢
//背包问题是每个物品都有一个价值和重量,然后当前重量下能装物品价值的最大值
//dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weigth[i]] + value[i]);
//这个问题是每个字符串都有0和1两个的数量,然后求当前要求下所装物品的最大数量
int l = strs.length;
int[][][] dp = new int[l + 1][m + 1][n + 1];
for(int i = 1; i <= l; i++){
int count0 = 0;
for(int j = 0; j < strs[i - 1].length(); j++){
count0 += strs[i - 1].charAt(j) == '0' ? 1 : 0;
}
int count1 = strs[i - 1].length() - count0;
for(int j = 0; j <= m; j++){
for(int k = 0; k <= n; k++){
//不选当前字符串
dp[i][j][k] = dp[i - 1][j][k];
//选当前字符串
if(j >= count0 && k >= count1){
dp[i][j][k] = Math.max(dp[i][j][k], dp[i - 1][j - count0][k - count1] + 1);
}
}
}
}
return dp[l][m][n];
}
}
状态压缩,注意内层循环反向遍历
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//一看好像就是动态规划,但是好像不知道怎么确定目标
//倒是可以用暴力解法,就是把所有的可能都列举出来,然后看最大符合题意的数目是多少个
//看了一下题解,发现可以理解为有两个维度的目标 的背包问题
//dp[i][j]最多有i个0和j个1的的最大子集大小,这相当于一个滚动数组
//递推公式 dp[i][j] = max(dp[i - 当前0的个数][j- 当前1的个数] + 1,dp[i][j])
int[][] dp = new int[m + 1][n + 1];
//初始化:mn都为0时,放不进去东西,所以为0
dp[0][0] = 0;
//遍历
for(String str : strs){
int num0 = 0;
int num1 = 0;
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if(c == '0')
num0++;
else
num1++;
}
//内层循环反向遍历,i<num0 和 j<num1就不用遍历了,因为他俩都不变
for(int i = m; i >= num0; i--){
for(int j = n; j >= num1; j--){
dp[i][j] = Math.max(dp[i][j], dp[i - num0][j - num1] + 1);
}
}
}
return dp[m][n];
}
}
203. 移除链表元素
题目描述
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-linked-list-elements
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
迭代
class Solution {
public ListNode removeElements(ListNode head, int val) {
//递归写一下
if(head == null)
return null;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
ListNode cur = head;
while(cur != null){
if(cur.val == val){
cur = cur.next;
pre.next = cur;
}else{
pre = cur;
cur = cur.next;
}
}
return dummy.next;
}
}
递归
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
第 244 场力扣周赛
这场周赛感觉自己状态很不好,可能跟昨天乱七八糟玩了一天有关…
5776. 判断矩阵经轮转后是否一致
题目描述
给你两个大小为 n x n 的二进制矩阵 mat 和 target 。现 以 90 度顺时针轮转 矩阵 mat 中的元素 若干次 ,如果能够使 mat 与 target 一致,返回 true ;否则,返回 false 。
示例 1:
输入:mat = [[0,1],[1,0]], target = [[1,0],[0,1]]
输出:true
解释:顺时针轮转 90 度一次可以使 mat 和 target 一致。
示例 2:
输入:mat = [[0,1],[1,1]], target = [[1,0],[0,1]]
输出:false
解释:无法通过轮转矩阵中的元素使 equal 与 target 一致。
示例 3:
输入:mat = [[0,0,0],[0,1,0],[1,1,1]], target = [[1,1,1],[0,1,0],[0,0,0]]
输出:true
解释:顺时针轮转 90 度两次可以使 mat 和 target 一致。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/determine-whether-matrix-can-be-obtained-by-rotation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
今天这个题直接给我干懵了,我感觉之前也做过旋转矩阵的题,但是我觉得还是挺麻烦的,我感觉一个简单题,应该不会那么复杂,然后我就迷茫了。
最后在做完第二道题,看了第三道题想不出来以后,再回来做第一道题,然后用翻转矩阵的方法过了,虽然很繁琐,但是还是学到了点东西(注意还有不旋转的情况):
顺时针旋转90度,相当于先按主对角线翻转,然后左右翻转
顺时针旋转180度,相当于先上下翻转,然后左右翻转
顺时针旋转270度,相当于先按主对角线翻转,然后上下翻转
class Solution {
int m;
int n;
public boolean findRotation(int[][] mat, int[][] target) {
//模拟吧,最多旋转三次
m = mat.length;
n = mat[0].length;
int[][] temp = rotate1(mat);
if(equal(mat, target))
return true;
if(equal(target, temp))
return true;
temp = rotate2(mat);
if(equal(target, temp))
return true;
temp = rotate3(mat);
if(equal(target, temp))
return true;
return false;
}
public int[][] rotate1(int[][] mat){
int[][] temp = new int[m][n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
temp[j][i] = mat[i][j];
}
}
int[][] temp2 = new int[m][n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
temp2[i][n - 1 - j] = temp[i][j];
}
}
return temp2;
}
public int[][] rotate2(int[][] mat){
int[][] temp = new int[m][n];
leetcode 494. 目标和