专栏名称: 吴师兄学算法
和程序员小吴一起从初学者的角度学习算法,以动画的形式呈现解题的思路。每周四篇原创文章,期待你的鉴赏!
目录
相关文章推荐
51好读  ›  专栏  ›  吴师兄学算法

2025最新大厂薪资一览表

吴师兄学算法  · 公众号  ·  · 2025-01-07 11:10

正文

请到「今天看啥」查看全文


大家好,我是吴师兄。

前几天,一份从脉脉流出的最新大厂薪资地图,让社交圈炸了锅。

不少人感叹,“难怪年轻人削尖脑袋往大厂挤,就这薪资,搁谁不心动?”

但也有人说,大厂的高薪不过是 “卖命的代价”

先来看看研发岗的薪资, 大厂一线岗位是真的高

应届生动辄月薪20k起,3年经验月薪能到35k以上,10年往上直接奔着100k+。

更别提15薪、16薪,干12个月能拿15个月的钱, 年终奖一发,直接笑出声

有朋友戏称, “年终奖不是奖励,是续命。”

在高压的大厂环境中,很多人指望年终奖来给自己一个交代。

也有人说, “只有熬过年终奖那一天,你才能体会什么叫真正的松了一口气。”

但问题来了: 这高薪,是“高福利”还是“高代价”?

再看看应届生, 薪资直接吊打同龄人

有人调侃: “大厂的校招面试现场,和考公务员的考场没区别。”

确实,谁不想年纪轻轻就月入20k?这不仅是薪资,还有一种“社会标签”: “我进了大厂,我成功了。”

但别忘了, 这门票的代价可不低

拿Offer之前,要过简历筛选、技术面试、综合面试三道关,八股文刷上几百道都不稀奇。能挤进来的,都是硬实力+运气兼备的“勇士”。

可问题是, 勇士进了大厂,能不能全身而退?

为什么这么多人宁愿拼命也要进大厂?

有人说,这是“性价比”问题: 年轻人想用最好的年华换取最值钱的薪资

30岁之前进大厂,挣够一套房的首付,然后撤退。这套逻辑,听起来确实很香。

但问题是, 现实真的能让你体面地撤退吗?

越来越多的“过来人”表示, 大厂的经历确实是块敲门砖,但同时也是一把双刃剑

在大厂, 你能快速成长,但也可能迅速耗尽激情 你能拿到高薪,但也可能换来高压和“社畜化”

有个朋友总结得很扎心: “大厂的薪资地图确实诱人,但你得先问自己——能跑多久?”

说到底,大厂的薪资只是个工具, 生活的质量才是真正的目的

冲刺大厂无可厚非,但记得提醒自己,不要被“高薪幻觉”绑架。

年终奖再高,也填不满空洞的内心;工资再多,也换不回丢失的健康。

最后, 祝大家2025年都能找到适合自己的节奏 拿得下高薪,撑得起生活;进得了大厂,出得了自由!


你怎么看大厂的薪资?有自己的故事和观点吗?评论区聊聊吧!


回归我们公众号的主题, 每天掌握一道算法题

继续来一道和「校招」相关的算法原题。

本原题练习网址:www.algomooc.com

题目描述

一农夫带着 m 只羊, n 只狼过河,农夫有一条可载 x 只狼/羊的船

农夫在时或者羊的 数量大于狼时,狼不会攻击羊;农夫在不损失羊的情况下,运输几次可以完成运输?

只计算农夫去对岸的次数,回程时农夫不会运送羊和狼。

输入描述

输入参数为 m n x

m 为羊的数量、 n 为狼的数量、 x 为可载狼和羊的数量

输出描述

返回运输次数即可

如果无法完成运输返回 0

补充说明

农夫在或农夫离开后羊的数量大于狼的数量时,狼不会攻击羊。农夫不占用船的容量

示例

输入

5 3 3

输出

3

说明

第一次运 2 只狼

第二次运 3 只羊

第三次运 2 只羊和 1 只狼

解题思路

非常有意思的好题,很少见到这么好的题目了。

转化为搜索状态树最小层数问题

本题乍一看其实很难看出是一道图搜索题目,但如果我们从做出整个运送过程的示意图, 把每次选择后此岸情况、彼岸情况的状态当作一个节点,而每次选择都会转移到下一层节点更新到下一个状态 ,就会发现这实际上是一个状态树寻找最小搜索层数的问题。

以题目提供的示例为例,我们做出示意图

显然我们可以看出几个要点

  1. 这是一个搜索过程,每一次运送过程,我们都有多种可能的运送方案
  2. 有一些方案可能会和之前出现过的方案重复,可以进行排除,无需做重复计算
  3. 本题的要求是要找到最少运送次数,即是 找到这棵状态树的最小搜索层数 ,所以使用BFS要使用DFS更优。

节点的设计以及更新

确定这是一个状态树搜索问题之后,我们就需要设计出节点的储存方式了。

很显然,每一个节点状态仅由 4 个数据来表示, 此岸羊的数量、此岸狼的数量、彼岸羊的数量、彼岸狼的数量。

因此 4 个变量或者一个长度为 4 的列表来表示一个节点状态。

# 分别用索引0 1 2 3来表示此岸羊的数量、此岸狼的数量、彼岸羊的数量、彼岸狼的数量
node = [sheep_here, wolf_here, sheep_there, wolf_there]

假设我们使用长度为 4 的列表来表示当前节点状态 node ,那么当我们从一个节点更新到下一个节点的时候(即一次运送过程),还需要满足以下几个限制条件:

  1. 本次运送的载重不能超过船的最大载重量 X
  2. 本次运送的羊或狼不能超过此岸的羊或狼的数量
  3. 运送后,此岸羊的数量必须多于狼
  4. 运送后,彼岸羊的数量必须多于狼

我们可以构建出如下函数

def update(X, node):
    # 遍历本次运送的羊的数量,为船的最大载重量和此岸羊的数量的较小值
    for sheep_num in range(min(X+1, node[0]+1)):
        # 遍历本次运送的狼的数量
        # 由于羊已经选择了sheep_num只,所以狼的数量上限为X-sheep_num只和此岸狼的数量的较小值
        for wolf_num in range(min(X+1-sheep_num, node[1]+1)):
            # 得到本次运送后的下一个节点状态
            # 此岸减少sheep_num只羊和wolf_num只狼
            # 彼岸增加sheep_num只羊和wolf_num只狼
            nxt = [node[0]-sheep_num, node[1]-wolf_num, node[2]+sheep_num, node[3]+wolf_num]
            # 如果这个方案使得此岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次方案使得此岸的羊的数量为0,是可以选择的
            if nxt[0] != 0 and nxt[0] <= nxt[1]:
                continue
            # 如果这个方案使得彼岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次运送后,彼岸的羊的数量仍然为0,是可以选择的
            if nxt[2] != 0 and nxt[2] <= nxt[3]:
                continue

我们就完成了从当前状态穷举得到下一个状态的过程。

重复状态的排除

另一个重要的问题是,我们可能会经历非常多的重复状态,而这些状态的重复计算是没有必要的。

因为BFS就决定了, 一旦某一个状态在之前遇到过,则当前搜索到的状态一定不会比之前那次搜索的层数更低

此处重复状态的排除,类似于我们在常规的二维网格搜索过程中构建的那个 check_list

我们可以构建一个 seen 哈希集合,储存所有已经出现过的状态。

要注意哈希集合中只能储存不可变类型的数据结构,譬如字符串或者元组。

此处使用字符串来储存

nxt_str = ",".join(map(str, nxt))

我们可以在上述的代码中再加上关于 seen 的代码,得到更新的代码如下

def update(X, node, seen):
    # 遍历本次运送的羊的数量,为船的最大载重量和此岸羊的数量的较小值
    for sheep_num in range(min(X+1, node[0]+1)):
        # 遍历本次运送的狼的数量
        # 由于羊已经选择了sheep_num只,所以狼的数量上限为X-sheep_num只和此岸狼的数量的较小值
        for wolf_num in range(min(X+1-sheep_num, node[1]+1)):
            # 得到本次本次运送后的下一个节点状态
            # 此岸减少sheep_num只羊和wolf_num只狼
            # 彼岸增加sheep_num只羊和wolf_num只狼
            nxt = [node[0]-sheep_num, node[1]-wolf_num, node[2]+sheep_num, node[3]+wolf_num]
            # 如果这个方案使得此岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次方案使得此岸的羊的数量为0,是可以选择的
            if nxt[0] != 0 and nxt[0] <= nxt[1]:
                continue
            # 如果这个方案使得彼岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次运送后,彼岸的羊的数量仍然为0,是可以选择的
            if nxt[2] != 0 and nxt[2] <= nxt[3]:
                continue
            # 用字符串nxt_str表示下一个节点的状态
            nxt_str = ",".join(map(str, nxt))
            # 如果这个状态曾遇到过,则无需选择,直接跳过
            if nxt_str in seen:
                continue
            # 如果没有遇到过这个状态,则直接储存如哈希集合seen中
            seen.add(nxt_str)

代入BFS代码框架

核心过程基本已经完成,进一步优化 update() 函数,将队列 q 和判断答案的代码加入,即可得到以下函数

# 计算下一个状态nxt并且更新队列q的函数
def update(X, node, seen, q):
    # 设置全局变量isFind,用来标记是否找到了解
    global isFind
    # 遍历本次运送的羊的数量,为船的最大载重量和此岸羊的数量的较小值
    for sheep_num in range(min(X+1, node[0]+1)):
        # 遍历本次运送的狼的数量
        # 由于羊已经选择了sheep_num只,所以狼的数量上限为X-sheep_num只和此岸狼的数量的较小值
        for wolf_num in range(min(X+1-sheep_num, node[1]+1)):
            # 得到本次运送后的下一个节点状态
            # 此岸减少sheep_num只羊和wolf_num只狼
            # 彼岸增加sheep_num只羊和wolf_num只狼
            nxt = [node[0]-sheep_num, node[1]-wolf_num, node[2]+sheep_num, node[3]+wolf_num]
            # 如果下一个状态的羊和狼在此岸的数量均为0,则找到了最小搜索层数的解,修改isFind且退出函数
            if nxt[0] == 0 and nxt[1] == 0:
                isFind = True
                return
            # 如果这个方案使得此岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次方案使得此岸的羊的数量为0,是可以接受的
            if nxt[0] != 0 and nxt[0] <= nxt[1]:
                continue
            # 如果这个方案使得彼岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次运送后,彼岸的羊的数量仍然为0,是可以接受的
            if nxt[2] != 0 and nxt[2] <= nxt[3]:
                continue
            # 用字符串nxt_str表示下一个节点的状态
            nxt_str = ",".join(map(str, nxt))
            # 如果这个状态曾遇到过,则无需选择,直接跳过
            if nxt_str in seen:
                continue
            # 如果没有遇到过这个状态,则直接储存如哈希集合seen中
            seen.add(nxt_str)
            # 将下一个节点状态nxt储存入队列q中
            q.append(nxt)

进行一些变量初始化并将 update() 函数在BFS代码框架中使用,则基本可以得到完整的代码

# 初始化用于BFS过程的队列
q = deque()
# 最初的状态,此岸有n只羊和m只狼,彼岸有0只羊和0只狼
q.append([n, m, 00])
# 初始化检查状态是否出现过的哈希集合seen
seen = set()
# BFS过程的层数,初始化为0
level = 0
# 构建变量isFind,表示最终是否找到解
isFind = False

# BFS过程
while q:
    # 搜索层数+1
    level += 1
    # 获得当前层的节点个数
    qSize = len(q)
    # 遍历当前层的所有的节点node
    for _ in range(qSize):
        # 弹出队头元素,获得当前节点node
        node = q.popleft()
        # 遍历下一个状态,排除重复状态,更新队列q
        update(X, node, seen, q)
        # 如果找到解,退出上层循环
        if isFind:
            break

    # 如果找到解,退出上层循环
    if  isFind:
        break

# 如果有解,则isFind为True,输出level,否则输出0
print(level if isFind else 0)

代码

Python

# www.algomooc.com

from collections import deque

# 输入羊的初始数量n,狼的初始数量m,船的最大载重量X
n, m, X = map(int, input().split())


# 计算下一个状态nxt并且更新队列q的函数
def update(X, node, seen, q):
    # 设置全局变量isFind,用来标记是否找到了解
    global isFind
    # 遍历本次运送的羊的数量,为船的最大载重量和此岸羊的数量的较小值
    for sheep_num in range(min(X+1, node[0]+1)):
        # 遍历本次运送的狼的数量
        # 由于羊已经选择了sheep_num只,所以狼的数量上限为X-sheep_num只和此岸狼的数量的较小值
        for wolf_num in range(min(X+1-sheep_num, node[1]+1)):
            # 得到本次运送后的下一个节点状态
            # 此岸减少sheep_num只羊和wolf_num只狼
            # 彼岸增加sheep_num只羊和wolf_num只狼
            nxt = [node[0]-sheep_num, node[1]-wolf_num, node[2]+sheep_num, node[3]+wolf_num]
            # 如果下一个状态的羊和狼在此岸的数量均为0,则找到了最小搜索层数的解,修改isFind且退出函数
            if nxt[0] == 0 and nxt[1] == 0:
                isFind = True
                return
            # 如果这个方案使得此岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次方案使得此岸的羊的数量为0,是可以接受的
            if nxt[0] != 0 and nxt[0] <= nxt[1]:
                continue
            # 如果这个方案使得彼岸羊的数量小于等于狼的数量,则不可以选择,直接跳过
            # 注意,如果本次运送后,彼岸的羊的数量仍然为0,是可以接受的
            if nxt[2] != 0 and nxt[2] <= nxt[3]:
                continue
            # 用字符串nxt_str表示下一个节点的状态
            nxt_str = ",".join(map(str, nxt))
            # 如果这个状态曾遇到过,则无需选择,直接跳过
            if nxt_str in seen:
                continue
            # 如果没有遇到过这个状态,则直接储存如哈希集合seen中
            seen.add(nxt_str)
            # 将下一个节点状态nxt储存入队列q中
            q.append(nxt)

# 初始化用于BFS过程的队列
q = deque()
# 最初的状态,此岸有n只羊和m只狼,彼岸有0只羊和0只狼
q.append([n, m, 00])
# 初始化检查状态是否出现过的哈希集合seen
seen = set()
# BFS过程的层数,初始化为0
level = 0
# 构建变量isFind,表示最终是否找到解
isFind = False

# BFS过程
while q:
    # 搜索层数+1
    level += 1
    # 获得当前层的节点个数
    qSize = len(q)
    # 遍历当前层的所有的节点node
    for _ in range(qSize):
        # 弹出队头元素,获得当前节点node
        node = q.popleft()
        # 遍历下一个状态,排除重复状态,更新队列q
        update(X, node, seen, q)
        # 如果找到解,退出上层循环
        if isFind:
            break

    # 如果找到解,退出上层循环
    if isFind:
        break

# 如果有解,则isFind为True,输出level,否则输出0
print(level if isFind else 0)

Java

//www.algomooc.com
import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 读取输入:羊的数量n,狼的数量m,船的最大载重量X
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 初始羊的数量
        int m = scanner.nextInt(); // 初始狼的数量
        int X = scanner.nextInt(); // 船的最大载重量

        // 初始化队列,用于BFS过程
        Queue<int[]> q = new LinkedList<>();
        // 最初的状态:此岸有n只羊和m只狼,彼岸有0只羊和0只狼
        q.offer(new int[]{n, m, 00});
        // 初始化检查状态是否出现过的哈希集合seen
        Set seen = new HashSet<>();
        // BFS过程的层数,初始化为0
        int level = 0;
        // 构建变量isFind,表示最终是否找到解
        boolean isFind = false;

        // BFS过程
        while (!q.isEmpty()) {
            // 搜索层数+1
            level++;
            // 获得当前层的节点个数
            int qSize = q.size();

            // 遍历当前层的所有的节点node
            for (int i = 0; i < qSize; i++) {
                // 弹出队头元素,获得当前节点node
                int[] node = q.poll();
                // 遍历下一个状态,排除重复状态,更新队列q
                isFind = update(X, node, seen, q);

                // 如果找到解,退出上层循环
                if (isFind) break;
            }

            // 如果找到解,退出上层循环
            if (isFind) break;
        }

        // 如果有解,则isFind为true,输出level,否则输出0
        System.out.println(isFind ? level : 0);
    }

    // 计算下一个状态nxt并且更新队列q的函数
    private static boolean update(int X, int[] node, Set seen, Queue<int[]> q) {
        // 遍历本次运送的羊的数量
        for (int sheepNum = 0; sheepNum <= Math.min(X, node[0]); sheepNum++) {
            // 遍历本次运送的狼的数量
            for (int wolfNum = 0; wolfNum <= Math.min(X - sheepNum, node[1]); wolfNum++) {
                // 得到本次运送后的下一个节点状态
                int[] nxt = {
                    node[0] - sheepNum, // 此岸减少羊的数量
                    node[1] - wolfNum, // 此岸减少狼的数量
                    node[2] + sheepNum, // 彼岸增加羊的数量
                    node[3] + wolfNum  // 彼岸增加狼的数量
                };

                // 如果下一个状态的羊和狼在此岸的数量均为0,则找到了解
                if (nxt[0] == 0 && nxt[1] == 0) {
                    return true;
                }

                // 检查此岸羊的数量是否小于等于狼的数量(但不为0)
                if (nxt[0] != 0 && nxt[0] <= nxt[1]) {
                    continue;
                }

                // 检查彼岸羊的数量是否小于等于狼的数量(但不为0)
                if (nxt[2] != 0 && nxt[2] <= nxt[3]) {
                    continue;
                }

                // 用字符串表示下一个节点的状态
                String nxtStr = nxt[0] + "," + nxt[1] + "," + nxt[2] + "," + nxt[3];

                // 如果这个状态曾遇到过,则直接跳过
                if (seen.contains(nxtStr)) {
                    continue;
                }

                // 如果没有遇到过这个状态,则储存到哈希集合seen中
                seen.add(nxtStr);
                // 将下一个节点状态nxt储存到队列q中
                q.offer(nxt);
            }
        }
        return false;
    }
}

C++

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

// 将状态转化为字符串形式,用于哈希表存储
string stateToString(vector<int>& state) {
    ostringstream oss;
    for (size_t i = 0; i < state.size(); i++) {
        if (i > 0) oss << ",";
        oss << state[i];
    }
    return oss.str();
}

// 更新队列的函数
bool update(int X, vector<int>& node, unordered_set<string>& seen, queue<vector<int>>& q) {
    for (int sheep_num = 0; sheep_num <= min(X, node[0]); sheep_num++) {
        for (int wolf_num = 0; wolf_num <= min(X - sheep_num, node[1]); wolf_num++) {
            vector<int> nxt = {
                node[0] - sheep_num, // 此岸减少羊的数量
                node[1] - wolf_num, // 此岸减少狼的数量
                node[2] + sheep_num, // 彼岸增加羊的数量
                node[3] + wolf_num  // 彼岸增加狼的数量
            };

            if (nxt[0] == 0 && nxt[1] == 0) {
                return true// 找到解
            }

            if (nxt[0] != 0 && nxt[0] <= nxt[1]) continue// 此岸条件不合法
            if (nxt[2] != 0 && nxt[2] <= nxt[3]) continue// 彼岸条件不合法

            string nxt_str = stateToString(nxt);
            if (seen.count(nxt_str)) continue// 状态已访问过

            seen.insert(nxt_str);
            q.push(nxt);
        }
    }
    return false;
}

int main() {
    int n, m, X;
    cin >> n >> m >> X;

    queue<vector<int>> q;
    unordered_set<string> seen;

    q.push({n, m, 00});
    seen.insert(stateToString(q.front()));

    int level = 0;
    bool isFind = false;

    while (!q.empty()) {
        level++;
        int qSize = q.size();

        for (int i = 0; i < qSize; i++) {
            vector<int> node = q.front();
            q.pop();

            if (update(X, node, seen, q)) {
                isFind = true;
                break;
            }
        }

        if (isFind) break;
    }

    cout << (isFind ? level : 0) << endl;
    return 0;
}

C

#include 
#include 
#include 
#include 

// 定义常量
#define MAX_STATE 10000  // 最大状态数量
#define MAX_STR_LEN 50   // 状态字符串的最大长度

// 定义队列结构
typedef struct {
    int data[MAX_STATE][4];  // 存储状态数组
    int front, rear;         // 队列头和尾
} Queue;

// 队列初始化
void initQueue(Queue *q)  {
    q->front = q->rear = 0;
}

// 队列是否为空
bool isEmpty(Queue *q) {
    return q->front == q->rear;
}

// 队列入队
void enqueue(Queue *q, int state[4]) {
    for (int i = 0; i < 4; i++) {
        q->data[q->rear][i] = state[i];
    }
    q->rear++;
}

// 队列出队
void dequeue(Queue *q, int state[4]) {
    for (int i = 0; i < 4; i++) {
        state[i] = q->data[q->front][i];
    }
    q->front++;
}

// 哈希表,用于记录已访问的状态
typedef struct {
    char keys[MAX_STATE][MAX_STR_LEN];
    int size;
} HashSet;

// 哈希表初始化
void initHashSet(HashSet *set) {
    set->size = 0;
}

// 检查哈希表中是否包含某个状态
bool contains(HashSet *setchar *key) {
    for (int i = 0; i < set->size; i++) {
        if (strcmp(set->keys[i], key) == 0) {
            return true;
        }
    }
    return false;
}

// 向哈希表中添加状态
void add(HashSet *setchar *key) {
    if (!contains(set, key)) {
        strcpy(set->keys[set->size++], key);
    }
}

// 将状态转化为字符串形式
void stateToString(int state[4], char *result) {
    sprintf(result, "%d,%d,%d,%d", state[0], state[1], state[2], state[3]);
}

// 更新队列的函数
bool update(int X, int node[4], HashSet *seen, Queue *q) {
    for (int sheep_num = 0; sheep_num <= X && sheep_num <= node[0]; sheep_num++) {
        for (int wolf_num = 0; wolf_num <= X - sheep_num && wolf_num <= node[1]; wolf_num++) {
            int nxt[4] = {
                node[0] - sheep_num, // 此岸减少羊的数量
                node[1] - wolf_num,  // 此岸减少狼的数量
                node[2] + sheep_num, // 彼岸增加羊的数量
                node[3] + wolf_num   // 彼岸增加狼的数量
            };

            // 如果下一个状态的羊和狼在此岸的数量均为0,则找到了解
            if (nxt[0] == 0 && nxt[1] == 0) {
                return true;
            }

            // 检查此岸羊的数量是否小于等于狼的数量(但不为0)
            if (nxt[0] != 0 && nxt[0] <= nxt[1]) {
                continue;
            }

            // 检查彼岸羊的数量是否小于等于狼的数量(但不为0)
            if (nxt[2] != 0 && nxt[2] <= nxt[3]) {
                continue;
            }

            // 用字符串表示下一个节点的状态
            // 在C语言中反复的字符串操作可能会导致超时
            // 可以改为用整数进行状态压缩来优化代码
            char nxtStr[MAX_STR_LEN];
            stateToString(nxt, nxtStr);

            // 如果这个状态曾遇到过,则直接跳过
            if (contains(seen, nxtStr)) {
                continue;
            }

            // 如果没有遇到过这个状态,则储存到哈希表中
            add(seen, nxtStr);
            // 将下一个节点状态存储到队列中
            enqueue(q, nxt);
        }
    }
    return false;
}

int main() {
    int n, m, X;
    // 输入羊的数量、狼的数量、船的最大载重量
    scanf("%d %d %d", &n, &m, &X);

    // 初始化队列和哈希表
    Queue q;
    initQueue(&q);
    HashSet seen;
    initHashSet(&seen);

    // 初始状态
    int startState[4] = {n, m, 00};
    enqueue(&q, startState);

    char startStr[MAX_STR_LEN];
    stateToString(startState, startStr);
    add(&seen, startStr);

    int level = 0;
    bool isFind = false;

    // BFS过程
    while (!isEmpty(&q)) {
        level++;
        int qSize = q.rear - q.front;

        for (int i = 0; i < qSize; i++) {
            int node[4];
            dequeue(&q, node);

            if (update(X, node, &seen, &q)) {
                isFind = true;
                break;
            }
        }

        if (isFind) break;
    }

    // 输出结果
    printf("%d\n", isFind ? level : 0);
    return 0;
}

Node JavaScript

const readline = require('readline');

// 创建接口以读取标准输入
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// 主函数
function main(input{
    // 读取输入:羊的数量 n,狼的数量 m,船的最大载重量 X
    const [n, m, X] = input.split(' ').map(Number);

    // 初始化队列,用于 BFS 过程
    const q = [];
    // 最初的状态:此岸有 n 只羊和 m 只狼,彼岸有 0 只羊和 0 只狼
    q.push([n, m, 00]);
    // 初始化检查状态是否出现过的哈希集合 seen
    const seen = new Set();
    // BFS 过程的层数,初始化为 0
    let level = 0;
    // 构建变量 isFind,表示最终是否找到解
    let isFind = false;

    // BFS 过程
    while (q.length > 0) {
        // 搜索层数 +1
        level++;
        // 获得当前层的节点个数
        const qSize = q.length;

        // 遍历当前层的所有节点
        for (let i = 0; i < qSize; i++) {
            // 弹出队列头部元素,获得当前节点 node
            const node = q.shift();

            // 遍历下一个状态,排除重复状态,更新队列 q
            if (update(X, node, seen, q)) {
                isFind = true;
                break;
            }
        }

        // 如果找到解,退出上层循环
        if (isFind) break;
    }

    // 如果有解,则 isFind 为 true,输出 level,否则输出 0
    console.log(isFind ? level : 0);
}

// 计算下一个状态并且更新队列 q 的函数
function update(X, node, seen, q{
    // 遍历本次运送的羊的数量
    for (let sheepNum = 0; sheepNum <= Math.min(X, node[0]); sheepNum++) {
        // 遍历本次运送的狼的数量
        for (let wolfNum = 0; wolfNum <= Math.min(X - sheepNum, node[1]); wolfNum++) {
            // 得到本次运送后的下一个节点状态
            const nxt = [
                node[0] - sheepNum, // 此岸减少羊的数量
                node[1] - wolfNum,  // 此岸减少狼的数量
                node[2] + sheepNum, // 彼岸增加羊的数量
                node[3] + wolfNum   // 彼岸增加狼的数量
            ];

            // 如果下一个状态的羊和狼在此岸的数量均为 0,则找到了解
            if (nxt[0] === 0 && nxt[1] === 0) {
                return true;
            }

            // 检查此岸羊的数量是否小于等于狼的数量(但不为 0)
            if (nxt[0] !== 0 && nxt[0] <= nxt[1]) {
                continue;
            }

            // 检查彼岸羊的数量是否小于等于狼的数量(但不为 0)
            if (nxt[2] !== 0 && nxt[2] <= nxt[3]) {
                continue;
            }

            // 用字符串表示下一个节点的状态
            const nxtStr = `${nxt[0]},${nxt[1]},${nxt[2]},${nxt[3]}`;

            // 如果这个状态曾遇到过,则直接跳过
            if (seen.has(nxtStr)) {
                continue;
            }

            // 如果没有遇到过这个状态,则储存到哈希集合 seen 中
            seen.add(nxtStr);
            // 将下一个节点状态 nxt 储存到队列 q 中
            q.push(nxt);
        }
    }
    return false;
}

// 监听输入并调用主函数
rl.on('line', (input) => {
    main(input.trim());
    rl.close();
});

Go

package main

import (
        "bufio"
        "fmt"
        "os"
        "strconv"
        "strings"
)

// 将状态转化为字符串形式,用于哈希集合存储
func stateToString(state []int) string {
        strs := make([]stringlen(state))
        for i, v := range state {
                strs[i] = strconv.Itoa(v)
        }
        return strings.Join(strs, ",")
}

// 更新队列的函数
func update(X int, node []int, seen map[string]bool, q *[][]int) bool {
        for sheepNum := 0; sheepNum <= min(X, node[0]); sheepNum++ {
                for wolfNum := 0; wolfNum <= min(X-sheepNum, node[1]); wolfNum++ {
                        nxt := []int{
                                node[0] - sheepNum, // 此岸减少羊的数量
                                node[1] - wolfNum,  // 此岸减少狼的数量
                                node[2] + sheepNum, // 彼岸增加羊的数量
                                node[3] + wolfNum,  // 彼岸增加狼的数量
                        }

                        if nxt[0] == 0 && nxt[1] == 0 {
                                return true // 找到解
                        }

                        if nxt[0] != 0 && nxt[0] <= nxt[1] {
                                continue // 此岸条件不合法
                        }
                        if nxt[2] != 0 && nxt[2] <= nxt[3] {
                                continue // 彼岸条件不合法
                        }

                        nxtStr := stateToString(nxt)
                        if seen[nxtStr] {
                                continue // 状态已访问过
                        }

                        seen[nxtStr] = true
                        *q = append(*q, nxt)
                }
        }
        return false
}

func min(a, b int) int {
        if a < b {
                return a
        }
        return b
}

func main() {
        scanner := bufio.NewScanner(os.Stdin)
        scanner.Scan()
        input := scanner.Text()

        // 读取输入:羊的数量、狼的数量、船的最大载重量
        parts := strings.Fields(input)
        n, _ := strconv.Atoi(parts[0])
        m, _ := strconv.Atoi(parts[1])
        X, _ := strconv.Atoi(parts[2])

        // 初始化队列和哈希集合
        q := [][]int{{n, m, 0, 0}}
        seen := make(map[string]bool)
        seen[stateToString(q[0])] = true

        level := 0
        isFind := false

        for len(q) > 0 {
                level++
                qSize := len(q)

                for i := 0; i < qSize; i++ {
                        node := q[0]
                        q = q[1:]

                        if update(X, node, seen, &q) {
                                isFind = true
                                break
                        }
                }

                if isFind {
                        break
                }
        }

        if isFind {
                fmt.Println(level)
        } else {
                fmt.Println(0)
        }
}

END

一个人盲目刷题会很难,但一群人可以刷得很快。

吴师兄的算法训练营已经陪伴了数千名学员,如果你也想高效刷题,稳稳拿下大厂 offer, 戳链接 🔗 加入我们吧!

这是一个 算法刷题指导 + 高频面试题解析 + 大厂真题讲解 + 模拟机试 + 持续迭代 的算法训练营:

  • 300 道 LeetCode 动画题解 ,小白也能轻松看懂!

  • 每周直播答疑 ,问题不过夜,疑惑随时解决!

  • 班群打卡互动 ,陪你坚持学习不掉队!

  • 大厂真题 & 面试技巧 ,让你少走弯路,直达 offer!

课程更新免费跟进,报名一次,五年有效!同学们的好评已经刷爆了!

如果你觉得内容对你有帮助,不妨随手点个赞、在看、转发三连吧 🌟

最后,送你一句话:跟着吴师兄,算法学习好轻松,吴师兄和算法训练营在你身边~







请到「今天看啥」查看全文


推荐文章
不正常人类研究中心  ·  我能怎么办?!我也很绝望啊
8 年前
徐文兵  ·  《飲食滋味》課程簡介
7 年前