程序员老鬼

同事原来20K,跳槽去了一个月薪25K的,结果项目被叫停,组内全员裁撤!然后重新找工作,现在3个多月了还在家里。

今天看到一个网友的吐槽,感觉真是……唉

是这样的:某位同事在原公司拿着20K的工资,突然跳槽去了一家公司,月薪一下子涨到了25K。听起来不错吧?薪水涨了,未来看起来也很美好。但没过多久,那个项目被叫停了,结果整个团队都被裁了。这可真是,猝不及防的打击。

Image

更离谱的是,这位同事现在已经有三个月没找到新工作,依旧在家里。你说这事情是不是挺有意思的?😅

说实话,这也让我想到了跳槽时的风险。大家都知道,薪资涨得快也没什么不对,但跳槽之前要考虑清楚,不单单是眼前的工资,还要看公司稳定性、行业前景以及个人发展。

所以嘛,跳槽要谨慎,薪水虽高,但能不能稳住饭碗更重要。不然最终就成了“家里蹲”一族了【备注:文末可领最新资料】。

算法题:两个子序列的最大点积

今天又来跟大家聊聊一道有点“烧脑”的算法题。这题说实话,刚开始看觉得有点头疼,但只要理解了其中的思想,就能迎刃而解。好了,不废话,直接进入正题。

题目要求我们找到两个子序列的最大点积。先来复习一下“点积”这个概念。假设我们有两个向量,A = [a1, a2, ..., an] 和 B = [b1, b2, ..., bn],它们的点积就是对应元素相乘后的和,即:
A · B = a1 * b1 + a2 * b2 + ... + an * bn

回到题目,我们的目标是找到两个子序列,它们的点积最大。首先,子序列的定义大家应该都清楚吧,就是从原序列中删除某些元素(可能一个也不删)而不改变其相对顺序,形成的新序列。

那么,问题来了,如何通过遍历所有的子序列来找到最大点积呢?那可不行!直接暴力解法的时间复杂度太高,能不能找到更高效的解法呢?

在思考这个问题时,我们可以借助动态规划来优化计算。核心思想是,假设我们有两个数组 A 和 B,我们可以通过动态规划(DP)来递归地求解每一对子序列的最大点积。

思路分析

我们定义一个二维 DP 数组 dp[i][j],表示从 A[i] 和 B[j] 开始,计算它们的最大点积。

状态转移方程的意思是:

  1. 如果我们选择 A[i] 和 B[j] 作为当前的子序列元素,dp[i][j] 就等于 dp[i+1][j+1](即跳到下一个元素)加上 A[i] * B[j]。
  2. 如果我们不选择其中一个元素,就分别尝试选择下一个元素的情况,更新 dp[i][j]。

算法步骤

  1. 初始化 DP 数组:dp[i][j] 表示从 A[i] 和 B[j] 开始,计算的最大点积。
  2. 状态转移:对于每一对 (i, j),我们可以选择将 A[i] 和 B[j] 放入子序列中,或者跳过它们,最终返回最大值。
  3. 边界条件:当我们遍历到 A 或 B 的末尾时,返回 0。

代码实现

public class MaxDotProduct {

    public static int maxDotProduct(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;

                // 初始化 dp 数组,注意这里的 dp 数组大小是 (m+1) * (n+1)
        int[][] dp = new int[m + 1][n + 1];

                // 初始化 dp 数组,填充为最小值,用来辅助求最大值
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                dp[i][j] = Integer.MIN_VALUE;
            }
        }

                // 递推公式填充 dp 数组
        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                dp[i][j] = Math.max(dp[i + 1][j + 1] + A[i] * B[j], Math.max(dp[i + 1][j], dp[i][j + 1]));
            }
        }

        // 最终返回 dp[0][0] 即为所求的最大点积
        return dp[0][0];
    }

    public static void main(String[] args) {
        int[] A = {1, 3, -5};
        int[] B = {-2, 4, 1};

                System.out.println("最大点积: " + maxDotProduct(A, B)); // 输出结果
    }
}

代码解释

  1. 初始化:我们先定义了一个大小为 (m+1) * (n+1) 的 DP 数组,m 是 A 的长度,n 是 B 的长度。我们给 dp[i][j] 赋初值为 Integer.MIN_VALUE,目的是为了在后面的比较中找到最大值。

  2. 状态转移:在计算 dp[i][j] 时,考虑两种选择:

  • 选择当前的 A[i] 和 B[j],那么点积就是 A[i] * B[j] 加上剩余部分的最大点积 dp[i+1][j+1]。
  • 跳过当前的 A[i] 或 B[j],分别计算跳过后的最大点积。
  • 最终答案:我们需要的答案就在 dp[0][0] 位置,它保存了从 A[0] 和 B[0] 开始的最大点积。

  • 复杂度分析

    • 时间复杂度:O(m * n),我们遍历了 A 和 B 的所有元素进行状态转移。
    • 空间复杂度:O(m * n),我们需要一个大小为 (m+1) * (n+1) 的 DP 数组来存储状态。

    总结

    通过动态规划,我们能够高效地求解这个问题,而不是通过暴力枚举所有子序列。其实,这道题的关键就是如何通过状态转移来减少冗余计算。程序员的世界里,最重要的就是要学会找到问题的本质,拿到“高效”的解决方案,这样才能让代码跑得更快、效率更高。

    最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek

    也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。

    -END-

    ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学找我领取。

    图片


    以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言。