CSP-J备考冲刺:深度优先搜索(DFS)在近年T3真题中的妙用
开心田螺
2025-10-27 00:32:40
0

CSP-J备考冲刺:深度优先搜索(DFS)在近年T3真题中的妙用

高效掌握DFS,助力信息学竞赛

各位备战CSP-J的同学们,11月份的竞赛即将到来,相信大家都在紧张备考中。今天老马将带大家深度剖析深度优先搜索(DFS)这一重要算法在CSP-J近年T3题目中的应用。

为什么DFS如此重要?

深度优先搜索是信息学竞赛中最基础也是最重要的算法之一,几乎每年都会在CSP-J的T3或T4题目中出现。

掌握DFS不仅能够解决搜索类问题,还能为学习更高级的回溯、剪枝算法打下坚实基础。

真题解析1:CSP-J 2024 小木棍

【提交】

https://www.luogu.com.cn/problem/P11229

【问题描述】

小 S 喜欢收集小木棍。在收集了 根长度相等的小木棍之后,他闲来无事,便用它们拼起了数字。用小木棍拼每种数字的方法如下图所示。

现在小 S 希望拼出一个整数,满足如下条件:

  • 拼出这个数恰好使用 根小木棍;

  • 拼出的数没有前导

  • 在满足以上两个条件的前提下,这个数尽可能小。

小 S 想知道这个数是多少,可 很大,把木棍整理清楚就把小 S 折腾坏了,所以你需要帮他解决这个问题。如果不存在正整数满足以上条件,你需要输出 进行报告。

【输入描述】

本题有多组测试数据。

输入的第一行包含一个正整数 ,表示数据组数。

接下来包含 组数据,每组数据的格式如下:

一行包含一个整数 ,表示木棍数。

【输出描述】

对于每组数据:输出一行,如果存在满足题意的正整数,输出这个数;否则输出

【样例输入1】

5

1

2

3

6

18

【样例输出1】

-1

1

7

6

208

【样例解释1】

  • 对于第一组测试数据,不存在任何一个正整数可以使用恰好一根小木棍摆出,故输出

  • 对于第四组测试数据,注意 并不是一个满足要求的方案。摆出 以及 都恰好需要 根小木棍,但它们不是摆出的数最小的方案。

  • 对于第五组测试数据,摆出 需要 根小木棍。可以证明摆出任何一个小于 的正整数需要的小木棍数都不是 。注意尽管拼出 也需要 根小木棍,但因为这个数有前导零,因此并不是一个满足要求的方案。

【数据范围】

对于所有测试数据,保证:

特殊性质 A:保证 的倍数且

特殊性质 B:保证存在整数 使得 ,且

参考答案:

这是一个典型的DFS+剪枝+贪心问题。我们需要在满足条件的解空间中,找到最小的正整数。

/*

* [CSP-J 2024] 小木棍

* https://www.luogu.com.cn/problem/P11229

*/

# include

# include

using namespace std;

intarr[10] = { 6,2,5,5,4,5,6,3,7,6};

intidx;//位数

voiddfs(intx, intrest)

{

// x表示第x位

// rest表示剩下rest个小木棍

if(x == idx + 1)

{

for(inti = 1; i <= idx; i++)

{

cout<< res[i];

}

cout<< endl;

return;

}

for(inti = 0; i < 10; i++)

{

if(i == 0&& x == 1)

{

continue;

}

// 只要i满足条件,填到第x位,直接break

if(rest - arr[i] >= 2* (idx - x) && rest - arr[i] <= 7* (idx - x))

{

res[x] = i;

dfs(x + 1, rest - arr[i]);

break;

}

}

}

intmain

{

intt;

cin>> t;

for(inti = 0; i < t; i++)

{

intn;

cin>> n;

// 除了-1之外,任何小木棍数量都可以够成一个数字

if(n == 1)

{

cout<< -1<< endl;

continue;

}

//数字小,位数一定最少,位数最少,再去找小的数字

idx = ceil(n / 7.0);

dfs(1, n);

}

return 0;

}

真题解析2:CSP-J 2017 棋盘

【提交】

https://www.luogu.com.cn/problem/P3956

【问题描述】

有一个 的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。

任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 个金币。

另外, 你可以花费 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。

现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?

【输入描述】

第一行包含两个正整数 ,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。

接下来的 行,每行三个正整数 , 分别表示坐标为 的格子有颜色

其中 代表黄色,代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为 ,右下角的坐标为

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是 一定是有颜色的。

【输出描述】

一个整数,表示花费的金币的最小值,如果无法到达,输出 -1

【样例输入1】

5 7

1 1 0

1 2 0

2 2 1

3 3 1

3 4 0

4 4 1

5 5 0

【样例输出1】

8

【样例解释1】

棋盘的颜色如下表格所示,其中空白的部分表示无色。

开始,走到 不花费金币。

向下走到 花费 枚金币。

施展魔法,将 变为黄色,花费 枚金币。

走到 不花费金币。

走到 不花费金币。

走到 花费 枚金币。

走到 花费 枚金币。

施展魔法,将 变为黄色,花费 枚金币。

走到 不花费金币。

走到 花费 枚金币。

共花费 枚金币。

【样例输入2】

5 5

1 1 0

1 2 0

2 2 1

3 3 1

5 5 0

【样例输出2】

-1

【样例解释2】

棋盘的颜色如下表格所示,其中空白的部分表示无色。

走到 ,不花费金币。

走到 ,花费 金币。

施展魔法将 变为黄色,并从 走到 花费 金币。

走到 不花费金币。

只能施展魔法到达

而从以上四点均无法到达 ,故无法到达终点,输出

【数据范围】

对于 的数据,

对于 的数据,

对于 的数据,

参考答案:

需要在彩色棋盘上找到从左上角到右下角的最小花费路径,涉及颜色变化和魔法使用,这是一个典型的DFS+记忆化应用。

#include

#include

#include

#include

using namespace std;

intdx[4] = { 1, -1, 0, 0};

intdy[4] = { 0, 0, 1, -1};

intm, n;

vector<vector<int>> color; // 存储颜色:-1无色,0红色,1黄色

vector<vector<int>> minCost; // 到达每个格子的最小花费

voiddfs(intx, inty, intcost, boolusedMagic)

{

// 剪枝:如果当前花费已经大于已知最小花费,直接返回

if(cost >= minCost[x][y])

{

return;

}

minCost[x][y] = cost;

// 到达终点

if(x == m && y == m)

{

return;

}

for(inti = 0; i < 4; i++)

{

intnx = x + dx[i];

intny = y + dy[i];

// 越界检查

if(nx < 1|| nx > m || ny < 1|| ny > m)

{

continue;

}

// 情况1:目标格子有颜色

if(color[nx][ny] != -1) {

if(color[nx][ny] == color[x][y])

{

// 颜色相同,不花费金币

dfs(nx, ny, cost, false);

}

else

{

// 颜色不同,花费1金币

dfs(nx, ny, cost + 1, false);

}

}

// 情况2:目标格子无色,且当前未处于魔法产生的格子

elseif(usedMagic == false)

{

// 施展魔法,花费2金币,将无色格子变为当前格子的颜色

color[nx][ny] = color[x][y]; // 暂时改变颜色

dfs(nx, ny, cost + 2, true); // 标记为使用过魔法

color[nx][ny] = -1; // 回溯,恢复无色

}

}

}

intmain

{

cin>> m >> n;

// 初始化vector二维数组,大小为(m+1)×(m+1),初始值为-1

color.resize(m + 1, vector<int>(m + 1, -1));

// 初始化minCost为(m+1)×(m+1),初始值为INF

minCost.resize(m + 1, vector<int>(m + 1, INT_MAX));

// 读入有颜色的格子

for(inti = 0; i < n; i++)

{

intx, y, c;

cin>> x >> y >> c;

color[x][y] = c;

}

// 从起点开始DFS

dfs(1, 1, 0, false);

// 输出结果

if(minCost[m][m] == INT_MAX)

{

cout<< -1<< endl;

}

else

{

cout<< minCost[m][m] << endl;

}

return 0;

}

DFS备考技巧

  1. 状态设计是关键:合理定义DFS状态参数,包含问题的所有维度

  2. 剪枝是效率保障:及时剪去不可能产生最优解的分支

  3. 记忆化避免重复:对已计算的状态进行记录,提高效率

  4. 注意边界条件:递归终止条件要考虑全面

实战练习建议
  1. 重新实现上述两道真题,确保完全理解

  2. 尝试使用DFS解决迷宫问题、八皇后问题等经典题目

  3. 练习剪枝技巧,思考如何优化搜索效率

离比赛还有一段时间,只要大家坚持练习,掌握DFS的核心思想,一定能在CSP-J中取得好成绩!

老马温馨提示:DFS虽好,但也要注意避免栈溢出问题。在问题规模较大时,考虑使用迭代加深或BFS等替代方案。

祝各位考生备考顺利,赛场得意!

青少年编程竞赛交流

相关内容

热门资讯

理工科申请美国硕士各类问题汇总... 各位理工方向的小伙伴们! 今天整理一波理工科常咨询硕士申请问题,你可能也有同样疑惑,希望能帮到大家!...
CSP-J备考冲刺:深度优先搜... CSP-J备考冲刺:深度优先搜索(DFS)在近年T3真题中的妙用 “ 高效掌握DFS,助力信息学竞赛...
单招真题题库推荐,济南录取帮助... 靠谱的单招真题题库推荐:录取帮助力山东考生精准备考 在单招考试竞争日益激烈的今天,拥有一套靠谱的单招...
看丹观察丨跳马、种地、下厨!这... 正如该幼儿园园长所说“生活即教育”,唯有尊重儿童成长规律,善用本地资源,科学规划课程,才能创造出更加...
吉林大学,太宝藏了吧! 白山巍巍,松水汤汤。 在北国春城长春, 屹立着一所与共和国共同成长的高等学府。 她历经近八十载风雨洗...
第42届物理竞赛国家集训队名单... 摘要 今天,第42届全国中学生物理竞赛决赛比赛结束,为大家带来决赛真题及参考答案+ 国家集训队名单(...
作文课上的“一封家书” 办公室的抽屉里躺着一封未寄出的信,信纸边缘卷曲,歪斜的字迹里藏着一个留守儿童不曾说出口的心事——那是...
青春期孩子为什么对异性有好感 “儿子最近总对着手机笑,好像在和女生聊天”“女儿开始偷偷攒钱买礼物,说是给同桌的”“孩子放学非要等某...
联赛4连败!英超-萨拉赫破门难... 北京时间10月26日凌晨3时,2025-26赛季英超联赛第9轮继续进行,卫冕冠军利物浦做客宾福特社区...
校领导考察图书馆“学习专座”,... 校领导考察图书馆“学习专座” 空间升级提升奉贤校区保障功能 为进一步提升奉贤校区大保障系统功能,学校...