校招算法笔面试 | 校招笔面试真题-拜访

题目## 题目

题目链接

解题思路

这是一道使用DFS求最短路径方案数的问题,主要思路如下:

  1. 问题分析:

    • n × m n \times m n×m 的网格中寻找从经理(1)到商家(2)的最短路径
    • 需要统计最短路径的方案数
    • 有障碍物(-1)不能通过
  2. 解决方案:

    • 使用 DFS 遍历所有可能的路径
    • 使用 map 存储不同路径长度对应的方案数
    • 剪枝优化:长于当前最短路径的分支直接返回
  3. 关键点:

    • 使用 visited 数组避免重复访问
    • 四个方向的移动使用方向数组
    • 回溯时恢复 visited 状态

代码

class Solution {
private:
    // 存储(路径长度,方案数)映射
    map<int, int> pathCount;
    // 方向数组:右、下、上、左
    int dx[4] = {0, 1, -1, 0};
    int dy[4] = {1, 0, 0, -1};
    
    void dfs(vector<vector<int>>& grid, int x, int y, int dist, 
            vector<vector<int>>& visited, int n, int m) {
        // 边界检查
        if (x < 0 || x >= n || y < 0 || y >= m) return;
        
        // 剪枝条件:障碍物、已访问、路径过长
        if (grid[x][y] == -1 || visited[x][y] || 
            (!pathCount.empty() && dist > pathCount.begin()->first)) {
            return;
        }
        
        // 到达商家,记录方案
        if (grid[x][y] == 2) {
            pathCount[dist]++;
            return;
        }
        
        // 标记访问并继续搜索
        visited[x][y] = 1;
        for (int i = 0; i < 4; i++) {
            dfs(grid, x + dx[i], y + dy[i], dist + 1, visited, n, m);
        }
        // 回溯,取消标记
        visited[x][y] = 0;
    }
    
public:
    int countPath(vector<vector<int>>& CityMap, int n, int m) {
        vector<vector<int>> visited(n, vector<int>(m, 0));
        
        // 找到经理位置并开始搜索
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (CityMap[i][j] == 1) {
                    dfs(CityMap, i, j, 0, visited, n, m);
                }
            }
        }
        
        return pathCount.empty() ? 0 : pathCount.begin()->second;
    }
};
import java.util.*;

class Solution {
    // 存储(路径长度,方案数)映射
    private TreeMap<Integer, Integer> pathCount = new TreeMap<>();
    // 方向数组:右、下、上、左
    private int[] dx = {0, 1, -1, 0};
    private int[] dy = {1, 0, 0, -1};
    
    private void dfs(int[][] grid, int x, int y, int dist, 
                    boolean[][] visited, int n, int m) {
        // 边界检查
        if (x < 0 || x >= n || y < 0 || y >= m) return;
        
        // 剪枝条件:障碍物、已访问、路径过长
        if (grid[x][y] == -1 || visited[x][y] || 
            (!pathCount.isEmpty() && dist > pathCount.firstKey())) {
            return;
        }
        
        // 到达商家,记录方案
        if (grid[x][y] == 2) {
            pathCount.put(dist, pathCount.getOrDefault(dist, 0) + 1);
            return;
        }
        
        // 标记访问并继续搜索
        visited[x][y] = true;
        for (int i = 0; i < 4; i++) {
            dfs(grid, x + dx[i], y + dy[i], dist + 1, visited, n, m);
        }
        // 回溯,取消标记
        visited[x][y] = false;
    }
    
    public int countPath(int[][] CityMap, int n, int m) {
        boolean[][] visited = new boolean[n][m];
        
        // 找到经理位置并开始搜索
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (CityMap[i][j] == 1) {
                    dfs(CityMap, i, j, 0, visited, n, m);
                }
            }
        }
        
        return pathCount.isEmpty() ? 0 : pathCount.firstEntry().getValue();
    }
}
from collections import defaultdict

class Solution:
    def countPath(self, CityMap: List[List[int]], n: int, m: int) -> int:
        # 存储(路径长度,方案数)映射
        path_count = defaultdict(int)
        # 方向数组:右、下、上、左
        dx = [0, 1, -1, 0]
        dy = [1, 0, 0, -1]
        visited = [[False] * m for _ in range(n)]
        
        def dfs(x: int, y: int, dist: int) -> None:
            # 边界检查
            if x < 0 or x >= n or y < 0 or y >= m:
                return
                
            # 剪枝条件:障碍物、已访问、路径过长
            if (CityMap[x][y] == -1 or visited[x][y] or 
                (path_count and dist > min(path_count.keys()))):
                return
                
            # 到达商家,记录方案
            if CityMap[x][y] == 2:
                path_count[dist] += 1
                return
                
            # 标记访问并继续搜索
            visited[x][y] = True
            for i in range(4):
                dfs(x + dx[i], y + dy[i], dist + 1)
            # 回溯,取消标记
            visited[x][y] = False
        
        # 找到经理位置并开始搜索
        for i in range(n):
            for j in range(m):
                if CityMap[i][j] == 1:
                    dfs(i, j, 0)
        
        return path_count[min(path_count.keys())] if path_count else 0

算法及复杂度

  • 算法:深度优先搜索(DFS) + 回溯
  • 时间复杂度: O ( 4 n m ) \mathcal{O}(4^{nm}) O(4nm) - 最坏情况下需要遍历所有可能的路径
  • 空间复杂度: O ( n m ) \mathcal{O}(nm) O(nm) - 需要 visited 数组和递归栈空间

题目链接

解题思路

这是一道概率期望计算题目。给定 N N N 个人和 M M M 个物品,每个物品有一个容量上限 C [ i ] C[i] C[i],每个人对每个物品都有一个选择概率 P [ i ] [ j ] P[i][j] P[i][j]。需要计算最终未被选中的人数的期望。

解题步骤:

  1. 对每个物品分别计算其被选中 k k k 个人的概率( k k k 0 0 0 C [ i ] C[i] C[i]
  2. 使用动态规划的思想,逐个人更新概率分布
  3. 计算每个物品的期望选中人数,累加得到总的期望选中人数
  4. 用总人数 N N N 减去期望选中人数即为答案

代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

int main() {
    int N, M;
    cin >> N >> M;
    
    // 初始化容量数组和概率矩阵
    vector<int> C(M);
    vector<vector<float>> P(N, vector<float>(M));
    
    // 输入每个物品的容量
    for(int i = 0; i < M; i++) {
        cin >> C[i];
    }
    
    // 输入每个人选择每个物品的概率
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < M; j++) {
            cin >> P[i][j];
        }
    }
    
    vector<float> p_sel(N + 1);
    float E_sel = 0;
    
    // 对每个物品分别计算
    for(int i = 0; i < M; i++) {
        fill(p_sel.begin(), p_sel.end(), 0.0);
        p_sel[C[i]] = 1.0;
        
        // 对每个人进行概率更新
        for(int j = 0; j < N; j++) {
            for(int k = 0; k <= C[i]; k++) {
                if(k == 0)
                    p_sel[k] = p_sel[k] + p_sel[k+1] * P[j][i];
                else if(k < C[i])
                    p_sel[k] = p_sel[k] * (1-P[j][i]) + p_sel[k+1] * P[j][i];
                else
                    p_sel[k] = p_sel[k] * (1-P[j][i]);
            }
        }
        
        // 计算期望值
        for(int k = 0; k <= C[i]; k++) {
            E_sel += k * p_sel[k];
        }
    }
    
    printf("%.1f", N-E_sel);
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        
        // 初始化容量数组和概率矩阵
        int[] C = new int[M];
        float[][] P = new float[N][M];
        
        // 输入每个物品的容量
        for(int i = 0; i < M; i++) {
            C[i] = sc.nextInt();
        }
        
        // 输入每个人选择每个物品的概率
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < M; j++) {
                P[i][j] = sc.nextFloat();
            }
        }
        
        float[] p_sel = new float[N + 1];
        float E_sel = 0;
        
        // 对每个物品分别计算
        for(int i = 0; i < M; i++) {
            Arrays.fill(p_sel, 0.0f);
            p_sel[C[i]] = 1.0f;
            
            // 对每个人进行概率更新
            for(int j = 0; j < N; j++) {
                for(int k = 0; k <= C[i]; k++) {
                    if(k == 0)
                        p_sel[k] = p_sel[k] + p_sel[k+1] * P[j][i];
                    else if(k < C[i])
                        p_sel[k] = p_sel[k] * (1-P[j][i]) + p_sel[k+1] * P[j][i];
                    else
                        p_sel[k] = p_sel[k] * (1-P[j][i]);
                }
            }
            
            // 计算期望值
            for(int k = 0; k <= C[i]; k++) {
                E_sel += k * p_sel[k];
            }
        }
        
        System.out.printf("%.1f", N-E_sel);
    }
}
N, M = map(int, input().split())

# 初始化容量数组和概率矩阵
C = list(map(int, input().split()))
P = []
for _ in range(N):
    P.append(list(map(float, input().split())))

E_sel = 0

# 对每个物品分别计算
for i in range(M):
    p_sel = [0.0] * (N + 1)
    p_sel[C[i]] = 1.0
    
    # 对每个人进行概率更新
    for j in range(N):
        for k in range(C[i] + 1):
            if k == 0:
                p_sel[k] = p_sel[k] + p_sel[k+1] * P[j][i]
            elif k < C[i]:
                p_sel[k] = p_sel[k] * (1-P[j][i]) + p_sel[k+1] * P[j][i]
            else:
                p_sel[k] = p_sel[k] * (1-P[j][i])
    
    # 计算期望值
    for k in range(C[i] + 1):
        E_sel += k * p_sel[k]

print(f"{N-E_sel:.1f}")

算法及复杂度

  • 算法:动态规划 + 概率计算。通过动态规划更新每个状态的概率分布,最后计算期望值。
  • 时间复杂度: O ( M N ⋅ max ⁡ ( C ) ) \mathcal{O}(MN\cdot \max(C)) O(MNmax(C)),其中 M M M 是物品数, N N N 是人数, C C C 是最大容量。
  • 空间复杂度: O ( N ⋅ M ) \mathcal{O}(N\cdot M) O(NM),主要用于存储概率矩阵。

关键点解释

  1. p_sel[k] 表示选中 k k k 个人的概率分布
  2. 对每个人的概率更新分为三种情况:
    • k = 0 k=0 k=0:只能从 k + 1 k+1 k+1 转移过来
    • 0 < k < C [ i ] 0<k<C[i] 0<k<C[i]:可以保持不变或从 k + 1 k+1 k+1 转移
    • k = C [ i ] k=C[i] k=C[i]:只能保持不变
  3. 最终的期望值通过概率分布与对应人数的乘积求和得到
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值