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等替代方案。

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

青少年编程竞赛交流

相关内容

热门资讯

寒假预习必备:三年级下册语文电... 寒假即将来临,许多家长和学生都开始为新学期的学习做准备。特别是三年级的学生,语文作为一门基础学科,其...
2026春湘教版七年级地理下册... 2026 春湘教版七年级地理下册紧扣新课标核心素养要求,以 “大洲 — 地区 — 国家” 的递进式区...
“平台接单”的大学生家教,权益... 线上平台与微信群组成主流家教中介渠道,却出现信息费较高、退费难、遇纠纷维权难等新问题 “平台接单”的...
真相来了丨打着高校旗号的“教授... 寒假临近,一些打着高校名义的“教授内推”“‘寒假学堂’营”“招生咨询”等信息层出不穷,这些信息靠谱吗...
打着高校旗号的“教授内推”“付... 寒假临近,一些打着高校名义的“教授内推”“‘寒假学堂’营”“招生咨询”等信息层出不穷,这些信息靠谱吗...
在海南,这所大学打开了“境外教... 实习生 刘禹淇 中青报·中青网记者 任明超 “最吸引我的是这个项目兼顾了本土化学习和德国深造的机会,...
取消中小学期末统考,多地已通知 这两天,青岛、成都、北京等部分地区传出取消期末统考的消息,“多地取消高一高二期末统考”的词条也随之冲...
官方确认:歼10CE一举击落多... 近日,国家国防科技工业局新闻宣传办公室近日正式对外发布2025年度国防科技工业十大新闻。其中提到,5...
碧澜外国语小学外语节落幕 学子... 南都讯 记者任朝州近日,深圳市龙华区碧澜外国语小学第八届外语文化艺术节在欢声笑语中圆满落幕。本届艺术...
央视曝光打着名校旗号“教授内推... 据央视新闻1月11日报道,寒假临近,一些打着高校名义的“教授内推”“‘寒假学堂’营”“招生咨询”等信...