程序员老鬼

DeepSeek正在招聘的薪资水平!

今天聊个最近挺火的话题——DeepSeek的薪资曝光,真是有点震惊啊!

先来看看这家公司的影响力。

DeepSeek最近推出的R1模型,颠覆了AI的传统发展路径。GRPO算法的突破,不仅强化了学习的瓶颈,还让他们的性价比远超OpenAI的O1模型,算力不再是唯一标准,这可真是给整个AI行业带来了一剂强心针。

话说回来,DeepSeek的招聘信息也让我们这些程序员眼睛一亮。

公司招聘岗位非常广泛——从UI、客户端、全栈到算法、架构都有,不论你是哪个方向的大神,总能找到合适的位置。而且薪资也真是很诱人,普遍14薪,给人的感觉就是“敢给,你就敢来”!

Image

举个例子,像深度学习研究员(AGI方向)这个岗位,薪资高达50-80K·14薪,简直是想得多,赚得多。对于那些有深厚学习和编程基础的技术大佬,简直是一次不可多得的机会。

总的来说,DeepSeek不仅技术实力雄厚,薪资待遇也非常有竞争力。

如果你是想在AI领域大展拳脚的人,这可是个不容错过的好机会哦!【备注:文末可领最新资料】。

算法题:合并石头的最低成本

今天咱们来聊聊一个挺有意思的算法问题——合并石头的最低成本。

题目大致意思

有一排石头,每堆石头都有不同的数量,你可以选择相邻的两堆合并,合并的代价是这两堆石头的总和,最后要把所有石头合并成一堆,问最低成本是多少?

这题看着就像是个贪吃蛇游戏,先合小的,再合大的,好像贪心策略挺合理?但你试试就会发现,结果不一定最优。😅 这时候,动态规划(DP)就派上用场了。

代码实现

咱们直接上代码,先看看怎么用动态规划解决这个问题:

public class MergeStones {
    public int mergeStones(int[] stones, int k) {
        int n = stones.length;
        if ((n - 1) % (k - 1) != 0) return -1; // 如果无法合并成1堆,直接返回-1

                int[] prefixSum = new int[n + 1]; // 前缀和数组
        for (int i = 0; i < n; i++) {
            prefixSum[i + 1] = prefixSum[i] + stones[i];
        }

                int[][] dp = new int[n][n];

                for (int len = k; len <= n; len++) { // 枚举区间长度
            for (int i = 0; i + len - 1 < n; i++) {
                int j = i + len - 1;
                dp[i][j] = Integer.MAX_VALUE;
                for (int m = i; m < j; m += k - 1) { // 枚举分界点
                    dp[i][j] = Math.min(dp[i][j], dp[i][m] + dp[m + 1][j]);
                }
                if ((j - i) % (k - 1) == 0) { // 只有能合并成一堆时才加上总和
                    dp[i][j] += prefixSum[j + 1] - prefixSum[i];
                }
            }
        }

                return dp[0][n - 1];
    }

        public static void main(String[] args) {
        MergeStones solver = new MergeStones();
        int[] stones = {3, 2, 4, 1};
        int k = 2;
        System.out.println(solver.mergeStones(stones, k)); // 输出 20
    }
}

代码解析

  1. 前缀和优化:用 prefixSum 记录从 0 到 i 的石头总数,方便计算某一段的总和,而不是每次遍历。
  2. **动态规划 dp[i][j]**:表示把 stones[i] 到 stones[j] 合并成一堆的最小代价。
  3. 区间 DP 递推:
  • len 表示当前区间长度。
  • i, j 表示合并的区间起点和终点。
  • m 枚举合并位置,尝试不同的切分方案。
  • dp[i][j] += prefixSum[j + 1] - prefixSum[i] 这步确保了当前区间正好能合成一堆时才加上合并代价。

为什么不用贪心?

有人可能会说:“直接找最小的两堆合并不就完了?”这不就是赫夫曼树的思路吗?🤔 但其实这个问题不能直接用贪心,因为某些情况下,先合并小堆会导致后面大代价的合并,而动态规划能确保全局最优。

复杂度分析

  • 由于我们遍历了所有区间 O(n^2),每次尝试不同的 m 进行切分 O(n),所以时间复杂度是 **O(n^3)**。
  • 但是前缀和查询是 O(1),所以计算某段和不会拖慢整体效率。

这题有啥现实意义?

这个问题其实在实际应用里很常见,比如:

  • 文件合并:多个小文件合并成一个大文件,如何最小化磁盘 I/O 成本?
  • 内存分配:合并多个小块内存,减少碎片化。
  • 数据聚合:数据分区合并计算,比如数据库索引合并。

说白了,这种最优合并策略在各种场景下都能用得上,和我们优化代码逻辑的思路很像,少走弯路,尽可能减少冗余计算。

总结

  1. 这题不能贪心,因为局部最优不一定是全局最优。
  2. 需要用区间 DP,dp[i][j] 记录 stones[i] 到 stones[j] 的最小合并成本。
  3. 用前缀和优化计算合并代价,减少重复计算。

所以,写代码的时候也要像合并石头一样,少点弯路,多点优化,毕竟谁也不想被大 O 吃掉性能啊!

-END-

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

Image

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