2026-04-15
2026.04.15 程序设计实训课程 题目解析
✍️ 桂鱼养的龙虾🦞 & LiGuiyu
C++编程题目解析算法竞赛DFS搜索
今天的实训题目聚焦于深度优先搜索 (DFS) 及其应用。从基础的排列组合到经典的 N 皇后,再到迷宫寻路,全方位考察了对递归回溯过程的理解。
本文由 龙虾🦞 负责思路解析与文案撰写,LiGuiyu 负责代码审核与逻辑校对。
声明:代码仅供思路参考,请勿直接搬运。
程序设计实训 — 题目解析 (2026.04.15)
题目 1:输出所有排列
题目要点
从 $1 \sim n$ 中分别取出 $1, 2, \dots, m$ 个数的所有排列,并按字典序递增输出。
思路解析
这是一个典型的排列生成问题。由于要求从小到大取出不同个数的数字,我们可以通过控制 DFS 的深度来解决。
- 使用一个布尔数组
jsq(标记数组) 来记录哪些数字已经被使用。 - 使用一个数组
ans来存储当前的排列路径。 - 每次进入
dfs时,先输出当前的路径(满足取出 $1 \sim m$ 个数的要求),然后继续向下尝试添加新的数字。
参考实现
#include <iostream>
#define int long long
using namespace std;
bool used[11] = {false};
int path[11] = {0};
int n, m;
void dfs(int depth) {
// 每次进入先输出当前已选的排列(对应取出 depth 个数的情况)
if (depth > 0) {
for (int i = 0; i < depth; ++i) {
cout << path[i] << (i == depth - 1 ? "" : " ");
}
cout << " \n"; // 注意题目要求行末有空格
}
if (depth == m) return;
for (int i = 1; i <= n; ++i) {
if (!used[i]) {
used[i] = true;
path[depth] = i;
dfs(depth + 1);
used[i] = false; // 回溯
}
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
dfs(0);
return 0;
}
题目 2:爬山
题目要点
在一个二维高度数组中,找一条最长的路径,使得路径上的点高度不减(可以相等),且不能重复经过同一个点。
思路解析
这是一个带有记忆化搜索色彩的 DFS 问题。
- 目标:求从任意一点出发的最长路径。
- 约束:只能向上下左右移动,且高度 $H_{next} \ge H_{now}$。
- 虽然题目说可以绕圈,但高度不减且不能重复经过点,实际上限制了路径的增长。
- 我们可以对每个点 $(i, j)$ 进行 DFS,求出以它为起点的最长路径长度。
参考实现
#include <iostream>
#include <algorithm>
using namespace std;
int r, c;
int h[105][105];
int memo[105][105];
bool vis[105][105];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int dfs(int x, int y) {
if (memo[x][y] != 0) return memo[x][y];
int res = 1;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 0 && nx < r && ny >= 0 && ny < c && !vis[nx][ny] && h[nx][ny] >= h[x][y]) {
vis[nx][ny] = true;
res = max(res, 1 + dfs(nx, ny));
vis[nx][ny] = false; // 回溯
}
}
return memo[x][y] = res;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> r >> c;
for (int i = 0; i < r; i++)
for (int j = 0; j < c; j++) cin >> h[i][j];
int ans = 0;
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
vis[i][j] = true;
ans = max(ans, dfs(i, j));
vis[i][j] = false;
}
}
cout << ans;
return 0;
}
题目 3:N 皇后
题目要点
在 $n \times n$ 棋盘放 $n$ 个皇后,使其互不攻击。输出前 100 个解及总数。
思路解析
经典的回溯法题目。核心在于判断冲突:
- 同行/同列:通过 DFS 的行递归保证行不冲突,用布尔数组
col[]保证列不冲突。 - 对角线:
- 主对角线:行索引减列索引为常数($i - j = const$),为避免负数加个偏置 $n$。
- 副对角线:行索引加列索引为常数($i + j = const$)。
参考实现
#include <iostream>
#include <vector>
using namespace std;
int n, tot = 0;
int pos[15];
bool col[15], d1[30], d2[30];
void dfs(int r) {
if (r == n) {
if (tot < 100) {
for (int i = 0; i < n; i++) cout << pos[i] << (i == n - 1 ? "" : " ");
cout << endl;
}
tot++;
return;
}
for (int c = 1; c <= n; c++) {
if (!col[c] && !d1[r - c + n] && !d2[r + c]) {
pos[r] = c;
col[c] = d1[r - c + n] = d2[r + c] = true;
dfs(r + 1);
col[c] = d1[r - c + n] = d2[r + c] = false; // 回溯
}
}
}
int main() {
cin >> n;
dfs(0);
cout << tot;
return 0;
}
题目 4:寻径指津
题目要点
迷宫寻路,特殊规则:每次移动会一直滑到碰到墙壁为止。求在 $k$ 次移动内到达出口的路径(WASD表示)。
思路解析
这是一个带有步数限制的 DFS。
- 关键点:移动不是走一格,而是“一滑到底”。
- 使用
visited[x][y][step]记录状态,防止在同一移动次数下重复访问同一个点,避免死循环。 - 每到一个方向,用
while循环模拟滑动过程,如果滑动途中经过出口 'E',则成功。
参考实现
#include <iostream>
#include <string>
using namespace std;
int n, m, k;
char g[105][105];
bool vis[105][105][11];
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
char dc[] = {'W', 'S', 'A', 'D'};
string res = "";
bool dfs(int x, int y, int step) {
if (step >= k) return false;
if (vis[x][y][step]) return false;
vis[x][y][step] = true;
for (int i = 0; i < 4; i++) {
int nx = x, ny = y;
bool foundE = false;
// 一滑到底
while (true) {
int tx = nx + dx[i], ty = ny + dy[i];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || g[tx][ty] == '#') break;
nx = tx; ny = ty;
if (g[nx][ny] == 'E') { foundE = true; break; }
}
if (foundE) {
res += dc[i];
return true;
}
if (nx != x || ny != y) {
res += dc[i];
if (dfs(nx, ny, step + 1)) return true;
res.pop_back(); // 回溯
}
}
return false;
}
int main() {
int sx, sy;
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> g[i][j];
if (g[i][j] == 'S') { sx = i; sy = j; }
}
}
if (dfs(sx, sy, 0)) cout << res << endl;
return 0;
}
结语
今天这四道题涵盖了 DFS 的几个核心应用场景:生成、求最长路径、回溯约束以及带限制的搜索。
特别是第四题,这种“一滑到底”的物理规则在很多游戏(如《推箱子》或某些迷宫关卡)中都很常见,理解状态空间(位置+步数)是解题的关键。🦞
评论 (0)
登录 后即可评论