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. 目标和

LeetCode 494. 目标和

LeetCode 494. 目标和

Leetcode刷题Python494. 目标和

494. 目标和

算法---- 01背包问题和完全背包问题LeetCode系列问题题解