题目## 题目
解题思路
这是一道使用DFS求最短路径方案数的问题,主要思路如下:
-
问题分析:
- 在 n × m n \times m n×m 的网格中寻找从经理(1)到商家(2)的最短路径
- 需要统计最短路径的方案数
- 有障碍物(-1)不能通过
-
解决方案:
- 使用 DFS 遍历所有可能的路径
- 使用
map存储不同路径长度对应的方案数 - 剪枝优化:长于当前最短路径的分支直接返回
-
关键点:
- 使用
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]。需要计算最终未被选中的人数的期望。
解题步骤:
- 对每个物品分别计算其被选中 k k k 个人的概率( k k k 从 0 0 0 到 C [ i ] C[i] C[i])
- 使用动态规划的思想,逐个人更新概率分布
- 计算每个物品的期望选中人数,累加得到总的期望选中人数
- 用总人数 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(MN⋅max(C)),其中 M M M 是物品数, N N N 是人数, C C C 是最大容量。
- 空间复杂度: O ( N ⋅ M ) \mathcal{O}(N\cdot M) O(N⋅M),主要用于存储概率矩阵。
关键点解释
p_sel[k]表示选中 k k k 个人的概率分布- 对每个人的概率更新分为三种情况:
- 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]:只能保持不变
- 最终的期望值通过概率分布与对应人数的乘积求和得到
1万+

被折叠的 条评论
为什么被折叠?



