Leetcode 每日一题和每日一题的下一题刷题笔记 7/30

327 阅读5分钟

Leetcode 每日一题和每日一题的下一题刷题笔记 7/30

写在前面

这是我参与更文挑战的第7天,活动详情查看:更文挑战

快要毕业了,才发现自己被面试里的算法题吊起来锤。没办法只能以零基础的身份和同窗们共同加入了力扣刷题大军。我的同学们都非常厉害,他们平时只是谦虚,口头上说着自己不会,而我是真的不会。。。乘掘金鼓励新人每天写博客,我也凑个热闹,记录一下每天刷的前两道题,这两道题我精做。我打算每天刷五道题,其他的题目嘛,也只能强行背套路了,就不发在博客里了。

本人真的只是一个菜鸡,解题思路什么的就不要从我这里参考了,编码习惯也需要改进,各位如果想找刷题高手请教问题我觉得去找 宫水三叶的刷题日记 这位大佬比较好。我在把题目做出来之前尽量不去看题解,以免和大佬的内容撞车。

另外我也希望有得闲的大佬提供一些更高明的解题思路给我,欢迎讨论哈!

好了废话不多说开始第七天的前两道题吧!

2021.6.7 每日一题

494. 目标和

又是动态规划,这个月是。。。我也不知道这个月是什么月了,也许明天又开始前缀和了。

这题有一种非常实在的思路,我把你各种状态都列全了,然后一个个找过去。这种思路我就不讲了,刷算法面试题是为了巩固知识的,这种思路自己手敲一遍就够了。

代码就是官方题解里面的方法一,注意用回溯的思路可以少敲键盘。时间复杂度就是全都来一遍的 O(2n)O(2^\texttt{n}),少敲键盘,思路直接,效率也低。这种解法对时间和空间要求稍微严一点就被卡在门外不让进去了。

动态规划思路也不难,这个题先梳理一下各种条件,给出的数组里面全部元素的绝对值之和是 abs_sum,然后为了得到目标值 target,其中一些数前面带上了负号,这些负数的绝对值的和是 neg_sum,由此可得剩下的正数的和是 pos_sum = abs_sum - neg_sum,正数和负数总和是目标值,这样就有一个条件出来了,abs_sum - 2 * neg_sum = target,转化一下,neg_sum = (abs_sum - target) / 2,当然也可以设一个正数的绝对值之和,然后求出,和这里的原理一样,不赘述。这个条件必须要满足,同时这个数组本来的各个元素都是非负整数,这个条件里的每个数也都要是非负整数,如果不满足,那后面就不用数方案了,没有可行方案。要遍历数组里面的每一个数,假设当前选上的这个数叫 cur_num

还记得我说过的“容量”和“价值”吗,一样的思路,这里“容量”是目前负数的绝对值之和,设为 i,“价值”是可以达到容量的方案数 dp[i]。这里状态转移方程只需要一个自变量就够了,就是容量 i。这么说还是太抽象,容量是干什么的?现在想象一下求解这个问题的过程,是一步一步来的,最后一步,容量达到了前面那个条件求出的负数的绝对值之和 neg_sum,这个时候可以得到 target 了,问题就结束了,最终的方案数就得到了。然后往前看,比如我当前正在看 cur_num 这个数,要不要选这个数。首先要判断出来这个数到底能不能选对吧,假如这个数的绝对值已经超出了 neg_sum,想选也选不了。我只看能选的 cur_num,不能选的就不看了。在能选的 cur_num 里,容量 i 肯定要大于 cur_num,然后分情况讨论,现在是选还是不选。选上 cur_num,容量减少,要处理的问题转移为 dp[i] + dp[i - cur_num];不选,容量不变,方法数也不变,那看其他的数喽。

写一下状态转移方程

dp[i]={dp[i],i<cur_numdp[i]+dp[icur_num],icur_num,pick cur_numdp[i],icur_num,not pick cur_num\texttt{dp}[\texttt{i}] = {\left \{ \begin{array}{ll} \texttt{dp}[\texttt{i}], & \texttt{i} < \texttt{cur\_num} \\ \texttt{dp}[\texttt{i}] + \texttt{dp}[\texttt{i} - \texttt{cur\_num}], & \texttt{i} \geq \texttt{cur\_num}, pick \ \texttt{cur\_num} \\ \texttt{dp}[\texttt{i}], & \texttt{i} \geq \texttt{cur\_num}, not \ pick \ \texttt{cur\_num} \end{array} \right.}

到这里,思路就差不多了,可以写代码了。


class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int abs_sum = 0;
        for (int& num : nums) {
            abs_sum += num;
        }
        int double_neg_sum = abs_sum - target;
        if (double_neg_sum < 0 || double_neg_sum % 2 != 0) {
            return 0;
        }
        int neg_sum = double_neg_sum / 2;
        vector<int> dp(neg_sum + 1);
        dp[0] = 1;
        for (int& cur_num : nums) {
            for (int j = neg_sum; j >= cur_num; j--) {
                dp[j] += dp[j - cur_num];
            }
        }
        return dp[neg_sum];
    }
};

image.png

代码里隐含了“排除掉不能选的 cur_num”这一步,循环要保证循环变量合法对吧。然后,“假如能选 cur_num,但我就是不选,就是玩”这种情况也没必要写,不选就是不变嘛。

有没有发现我今天的讲法和昨天有些差别,我今天就是刻意在避开状态变量这个东西,意思就是正常人是可以直接想到减少空间复杂度的解法的。应该不难理解吧,如果还是不好懂,就自己加上一个状态变量,用 dp[i][j]\texttt{dp}[\texttt{i}][\texttt{j}] 自己推一遍,就好理解了。

我这样的写法,空间复杂度只有 O(neg_sum)O(\texttt{neg\_sum}),时间复杂度还是两层循环 O(n×neg_sum)O(\texttt{n} \times \texttt{neg\_sum}),这少不了的。

2021.6.7 每日一题下面的题

1091. 二进制矩阵中的最短路径

这道题用广度优先搜索,广度优先搜索配合栈,栈用来存当前这个点之后能往它周围八个点哪个点上面走,然后为了避免路线中出现环,在移动到一个可移动的 0 格子之前就把这个格子涂上 1 的标志,有点那种涂色占地盘游戏的感觉。出现环了以后有的用例里就一直在环里面转不出来了。。。


class Solution {
public:
    int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
        if (grid[0][0] != 0) {
            return -1;
        }
        int rows = grid.size();
        int cols = grid[0].size();
        queue<pair<int, int>> q;
        q.push({0, 0});
        grid[0][0] = 1;
        // direction type: l-left, r-right, u-up, d-down, p-pause 
        //                 [rp][pd][lp][pu][ru][rd][ld][lu]
        int diretions[] = {1,  0, -1,  0,  1,  1, -1, -1,  1};

        int hop = 0;
        while (!q.empty()) {
            hop++;
            for (int i = q.size(); i >= 1; i--) {
                int x = q.front().first;
                int y = q.front().second;
                q.pop();

                if (x == rows-1 && y == cols-1) {
                    return hop;
                }

                for (int k = 0; k < 8; k++) {
                    int nx = x + diretions[k];
                    int ny = y + diretions[k + 1];
                    if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == 0) {
                        grid[nx][ny] = 1;
                        q.push({nx, ny});
                    }
                }
            }
        }
        return -1;
    }
};

image.png

小结

背包问题,BFS

参考链接