蓝桥杯 DFS经典题 —— 算式900、寒假作业(告别枚举法)

类似于 算式 900、寒假作业这种题目,可以直接暴力破解,但是它非常的浪费时间,而且程序不是太过于美观,很容易让我们对算法失去兴趣,今天,我们告别传统的暴力破解,使用伟大搜索算法 —— DFS(深度优先搜索) . . .
.
DFS 相关文章如下所示:
《算法笔记》—— “迷宫求解” 之 深度优先搜索(DFS)
《算法笔记》—— 图 “邻接矩阵” 的遍历(DFS、BFS)
在这里插入图片描述


算式900

题目: 小明的作业本上有道思考题:

看下面的算式:

(□□□□-□□□□)*□□=900

其中的小方块代表 0~9 的数字,这10个方块刚好包含了 0 ~ 9 中的所有数字。
注意:0不能作为某个数字的首位

小明经过几天的努力,终于做出了答案!如下:
(5012-4987)*36=900

用计算机搜索后,发现还有另外一个解,本题的任务就是:请你算出这另外的一个解。

注意:提交的格式需要与示例严格一致;
括号及运算符号不要用中文输入法;
整个算式中不能包含空格。
.

很多人看到这种类型的题目,第一想法就是暴力破解,实则没必要,但是暴力破解快乐啊! 哈哈哈,看看下面的这个代码,你还快乐吗?
代码太长我直接贴图了:
在这里插入图片描述
看到这个代码你真的很快乐吗? 哈哈哈 ~ 其实我非常的痛苦,感觉脑子快要裂开了 (>_<) . . .

下面我们用 DFS 来解决一下这个题目,代码如下:

#include <iostream>
using namespace std;

size_t flag[10];  // 标记 10 个数字是否被使用过
size_t arr[10];	  // 存储 10 个满足题目意思的书

void Dfs(int n)	  // 深搜思想
{
    if (n == 10)  // 已经找到 10 个不重复的数	
    {
     	// 判断是否满足题目的意思  == 900
  	if (((arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]) -
   		(arr[4] * 1000 + arr[5] * 100 + arr[6] * 10 + arr[7])) *
   		(arr[8] * 10 + arr[9]) == 900)
  	{
   	    cout << " ( " << (arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]) << " - " <<
    		(arr[4] * 1000 + arr[5] * 100 + arr[6] * 10 + arr[7]) << " ) " << " * " <<
    		(arr[8] * 10 + arr[9]) << " = " << 900 << endl;
  	}
	
	return;		// 不要忘记了这个返回
    }
    
    for (size_t i = 0; i < 10; ++i)	// 每一个数都有 10 种可能
    {
        if (i == 0 && (n == 0 || n == 4 || n == 8))	// 个别的数只有 9 种可能  开头没有 0 的情况
   	    continue;
  	
  	if (flag[i] == 0)		// 这个数字当前没有被使用过
  	{
   	    flag[i] = 1;		// 标记为使用过
   	    arr[n] = i;			// 将这个数存入数组之中
   	    Dfs(n + 1);			// 下一个位置的深搜
   	    flag[i] = 0;		// 这个数没有用,取消标记(便于下个位置使用)
      	} 
    }
}

int main()
{
    Dfs(0);
    
    return 0;
}

使用 DFS 是不是非常的方便呢,结果如下所示:
在这里插入图片描述


.

寒假作业

题目:
现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:

□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □

每个方块代表 1 ~ 13 中的某一个数字,但不能重复。

思想过程: 这个题目我们继续使用 DFS来求解,但我们在 DFS的过程中,会对程序进行优化,即数字个数每够一个式子成立时,判断这个式子是否满足题意,比如3 个数字时 判断 + 的那个式子,6 个 数字时判断 - 的那个式子,9 个数字时判断 * 的那个式子,12 个数字时判断 / 的那个式子 . . .

优化过程,参考度娘上的大神 ^ _ ^

.

代码如下所示:

#include <iostream>
using namespace std;

int arr[4][3];
int flag[14];
int sum;

// 判断是否满足式子的要求,优化过程
int JudgeIsNoGoOn(int n)
{
    int x = n / 3;
    
    if (n % 12 == 2)   // n == 2 时,判断第一个式子是否满足条件,下面同理 
    {
  	if (arr[x][0] + arr[x][1] != arr[x][2]) return 0;
    }
    
    else if (n % 12 == 5)
    {
  	if (arr[x][0] - arr[x][1] != arr[x][2]) return 0;
    }
    
    else if (n % 12 == 8)
    {
  	if (arr[x][0] * arr[x][1] != arr[x][2]) return 0;
    }
 
    else if (n % 12 == 11)
    {
  	if (arr[x][2] * arr[x][1] != arr[x][0]) return 0;
  	
  	printf("%d + %d = %d\n", arr[0][0], arr[0][1], arr[0][2]);
  	printf("%d - %d = %d\n", arr[1][0], arr[1][1], arr[1][2]);
  	printf("%d * %d = %d\n", arr[2][0], arr[2][1], arr[2][2]);
  	printf("%d / %d = %d\n\n\n", arr[3][0], arr[3][1], arr[3][2]);
    }
    
    return 1;
}

// 与上面差不多的思想,加个判断而已 . . . 
void dfs(int n)
{
    int x = n / 3, y = n % 3;  // 需要存储的二维数组的索引 
    
    // 到最后一个数字了,判断是否满足题目要求
    if (x == 4) { if (JudgeIsNoGoOn(11) == 1) ++sum; }
    else
    {
  	for (int i = 1; i <= 13; ++i)  // 每一个空位 都有 13 种可能性 
  	{
   	    if (flag[i] == 0)
   	    {
    		flag[i] = 1;
    		arr[x][y] = i;
    		
    		// 每存放一位数字,就判断一次(可以前几个式子就不满足)
    		// 当前的数字不满足条件,标记数组值还原,进行下一次的循环    
    		if (JudgeIsNoGoOn(n) == 0) 
    		{
     		    flag[i] = 0;
     		    continue;
    		}
    		
    		dfs(n + 1);     // 下一位数字继续 
    		flag[i] = 0;
   	    }
  	} 
    }
}

熟悉的使用 DFS 或者 BFS,可以帮我们解决很多的问题,而且也有很大的成就感,哈哈 ~


.

浪子花梦

一个有趣的程序员 ~

<think>嗯,用户想找关于C++实现蓝桥杯DFS算法的示例与解析。首先,我需要回忆一下蓝桥杯常见的DFS型,比如全排列、迷宫问、连通块等。然后,要确保提供的代码是C++的,并且有详细解析。 先看用户提供的引用内容,引用2和4中有C++代码示例,但主要是暴力模拟和其他算法。引用5提到DFS可能用于枚举法,可以作为参考。我需要从自己的知识库中提取合适的例子,比如全排列或者迷宫路径问。 比如全排列问,适合DFS,可以展示递归和回溯的过程。然后迷宫问,检查路径是否存在,或者计算路径数量。这两个都是蓝桥杯常见的考点。 然后要确保代码符合C++标准,使用正确的语法,比如使用vector或者数组来存储访问状态。同时,解析部分需要分步骤解释DFS的实现,比如递归终止条件、状态标记、遍历可能的选择、回溯等。 另外,用户可能需要了解DFS的时间复杂度以及优化方法,比如剪枝。所以相关问可以涉及这些方面。 需要注意用户提供的引用中有没有相关的内容,比如引用5提到枚举法,和DFS中的穷举思路类似,可以联系起来。但主要还是要给出具体的C++示例,比如全排列的代码,并详细解释每一步的作用。 最后,生成的相关问应该围绕DFS的应用、优化、与其他算法的比较等,帮助用户深入理解。</think>### C++实现蓝桥杯DFS算法示例与解析 #### 示例一:全排列问 **目描述** 给定数字$n$,生成$1$到$n$的全排列。 **C++代码实现** ```cpp #include <iostream> #include <vector> using namespace std; vector<int> path; // 当前路径 vector<bool> used; // 标记数字是否使用过 void dfs(int n) { if (path.size() == n) { // 终止条件:已选完所有数字 for (int num : path) cout << num << " "; cout << endl; return; } for (int i = 1; i <= n; i++) { if (!used[i]) { // 选择未使用的数字 used[i] = true; // 标记已使用 path.push_back(i); dfs(n); // 递归下一层 path.pop_back(); // 回溯 used[i] = false; } } } int main() { int n = 3; used.resize(n + 1, false); dfs(n); return 0; } ``` **解析** 1. **状态标记**:`used`数组记录数字是否被使用,避免重复选择[^5]。 2. **递归终止**:当路径长度等于$n$时输出结果。 3. **回溯操作**:在递归返回后撤销当前选择,恢复状态。 --- #### 示例二:迷宫路径计数 **目描述** 在$5×5$迷宫中,从左上角到右下角,求合法路径数(只能向右或向下走)。 **C++代码实现** ```cpp #include <iostream> using namespace std; int maze[5][5] = {0}; // 0表示可通行 int count = 0; void dfs(int x, int y) { if (x == 4 && y == 4) { // 到达终点 count++; return; } // 向右探索 if (y + 1 < 5 && maze[x][y+1] == 0) { maze[x][y+1] = 1; // 标记已访问 dfs(x, y + 1); maze[x][y+1] = 0; // 回溯 } // 向下探索 if (x + 1 < 5 && maze[x+1][y] == 0) { maze[x+1][y] = 1; dfs(x + 1, y); maze[x+1][y] = 0; } } int main() { dfs(0, 0); cout << "Total paths: " << count << endl; return 0; } ``` **解析** 1. **方向控制**:通过限制移动方向(右/下)避免循环。 2. **剪枝优化**:通过`maze`数组标记已访问位置,防止重复访问。 --- #### 示例三:岛屿数量问 **目描述** 给定二维网格,计算由`1`组成的岛屿数量(假设网格四周被水包围)。 **C++代码实现** ```cpp #include <vector> using namespace std; void dfs(vector<vector<char>>& grid, int i, int j) { if (i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == '0') return; grid[i][j] = '0'; // 淹没当前陆地 dfs(grid, i+1, j); // 下 dfs(grid, i-1, j); // 上 dfs(grid, i, j+1); // 右 dfs(grid, i, j-1); // 左 } int numIslands(vector<vector<char>>& grid) { int count = 0; for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid[0].size(); j++) { if (grid[i][j] == '1') { dfs(grid, i, j); count++; } } } return count; } ``` **解析** 1. **连通块标记**:通过DFS将相连的`1`全部标记为`0`,每个连通块计数一次[^4]。 2. **四方向遍历**:上下左右四个方向探索陆地边界。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值