零.【摘要】:在这个专刊里,我会把所有算法都讲一遍,这章讲了搜索与回溯算法的原理和题目。
一.【搜索与回溯算法】:
搜索和回溯算法都是常用的问题求解方法,经常用于解决组合优化问题,如全排列、子集、组合等。以下是搜索和回溯算法的基本思想和示例代码:
-
搜索算法的基本思想是通过遍历搜索问题的所有可能解,找到满足条件的解。搜索算法可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。
-
回溯算法是一种特殊的深度优先搜索算法,在搜索过程中,当发现当前的部分解不能满足问题的条件时,就会回溯到上一个状态,然后进行其他的尝试。回溯算法通常使用递归来实现。
二.【搜索之广度优先搜索】:
广度优先搜索(BFS,Breadth-First Search)是一种用于图和树等数据结构中的搜索算法。它从开始节点开始,逐层遍历,先访问离起始节点最近的节点,然后逐渐扩展到离起始节点更远的节点。
三【广度优先搜索的做法】:
- 创建一个队列(可以使用STL中的
queue
)和一个visited数组(用于记录已经访问过的节点)。 - 将起始节点放入队列,并将其标记为已访问。
- 循环执行以下步骤,直到队列为空:
- 从队列中取出队首节点。
- 处理当前节点(例如,输出、记录或进行其他操作)。
- 遍历当前节点的邻居节点:
- 如果邻居节点未被访问过,则将其标记为已访问,并将其放入队列中。
- 完成搜索。
广度优先搜索的特点是按照节点的层次进行遍历,从起始节点开始,依次访问离起始节点最近的节点,然后再访问离起始节点较远的节点。
四.【广度优先搜索事例代码】:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
void bfs(vector<vector<int>>& graph, int start) {
int n = graph.size();
vector<bool> visited(n, false); // 记录已经访问过的节点
queue<int> q; // 用队列来实现广度优先搜索
visited[start] = true;
q.push(start);
while (!q.empty()) {
int node = q.front();
q.pop();
cout << node << " "; // 打印当前节点
// 遍历当前节点的邻居节点
for (int i = 0; i < graph[node].size(); i++) {
int neighbor = graph[node][i];
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
int main() {
// 示例图的邻接表表示
vector<vector<int>> graph = {
{1, 2},
{0, 3, 4},
{0, 5},
{1},
{1},
{2}
};
bfs(graph, 0); // 从0开始进行广度优先搜索
return 0;
}
输出结果为:0 1 2 3 4 5。
在示例代码中,我们使用一个 visited
数组来记录已经访问过的节点,避免重复访问同一个节点。同时使用一个队列 q
来保存待访问的节点。我们从起始节点开始,将其标记为已访问并入队,然后循环进行弹出队首节点并打印,同时将其未访问的邻居节点标记为已访问并入队,直到队列为空。这样就完成了广度优先搜索的图遍历。
五.【搜索之深度优先搜索】:
深度优先搜索(DFS,Depth-First Search)是一种用于图和树等数据结构中的搜索算法。它从起始节点开始,通过依次访问节点的子节点,直到达到最深的节点,然后再回溯到上一层继续进行搜索。
六.【深度优先搜索的做法】:
- 创建一个visited数组(用于记录已经访问过的节点)。
- 从起始节点开始,调用递归函数进行深度优先搜索。
- 在递归函数中,首先处理当前节点(例如,输出、记录或进行其他操作)。
- 遍历当前节点的邻居节点:
- 如果邻居节点未被访问过,则将其标记为已访问,并继续递归调用深度优先搜索函数。
- 完成搜索。
深度优先搜索的特点是优先访问离起始节点较远的节点,直到达到最深的节点,然后再回溯到上一层继续搜索。这种搜索方式类似于前序遍历树的方式。
七.【深度优先搜索事例代码】:
下面是一个使用深度优先搜索进行图遍历的示例代码:
#include <iostream>
#include <vector>
using namespace std;
void dfs(vector<vector<int>>& graph, vector<bool>& visited, int node) {
visited[node] = true;
cout << node << " "; // 处理当前节点
// 遍历当前节点的邻居节点
for (int i = 0; i < graph[node].size(); i++) {
int neighbor = graph[node][i];
if (!visited[neighbor]) {
dfs(graph, visited, neighbor); // 递归调用进行深度优先搜索
}
}
}
void dfsTraversal(vector<vector<int>>& graph, int start) {
int n = graph.size();
vector<bool> visited(n, false); // 记录已经访问过的节点
dfs(graph, visited, start); // 从起始节点开始进行深度优先搜索
}
int main() {
// 示例图的邻接表表示
vector<vector<int>> graph = {
{1, 2},
{0, 3, 4},
{0, 5},
{1},
{1},
{2}
};
dfsTraversal(graph, 0); // 从0开始进行深度优先搜索
return 0;
}
输出结果为:0 1 3 4 2 5。
在示例代码中,我们使用一个 visited
数组来记录已经访问过的节点,避免重复访问同一个节点。我们从起始节点开始,调用递归函数进行深度优先搜索。在递归函数中,首先处理当前节点,然后遍历当前节点的邻居节点,如果邻居节点未被访问过,则将其标记为已访问并继续递归调用深度优先搜索函数。这样就完成了深度优先搜索的图遍历。
八.【搜索之回溯】:
回溯(Backtracking)是一种在搜索算法中常用的技术,用于在问题的解空间中进行搜索并找到所有满足特定条件的解。它通常用于解决组合、排列、子集等相关问题。
九.【回溯的做法】:
- 定义一个解空间,即问题的可能解集合。
- 定义一个候选集,表示当前选择的候选项(可能是下一步可以选择的元素)。
- 使用递归函数进行回溯搜索:
- 终止条件:
- 如果满足特定条件(例如,找到了一个有效解),则记录当前解。
- 在候选集中选择一个元素作为下一步的选择,并将其添加到当前解中。
- 递归调用回溯函数,进行下一步的搜索。
- 撤销当前选择,恢复到上一层的状态,继续搜索下一个候选项。
- 终止条件:
- 返回所有找到的解。
十.【回溯事例代码】:
下面是一个使用回溯算法求解全排列问题的示例代码:
#include <iostream>
#include <vector>
using namespace std;
void backtrack(vector<int>& nums, vector<int>& path, vector<bool>& used, vector<vector<int>>& result) {
// 终止条件:当当前排列的长度达到原始数组的长度时,将当前排列加入到结果集中
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
if (!used[i]) {
used[i] = true;
path.push_back(nums[i]);
backtrack(nums, path, used, result);
path.pop_back();
used[i] = false;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
vector<int> path;
vector<bool> used(nums.size(), false);
backtrack(nums, path, used, result);
return result;
}
int main() {
vector<int> nums = {1, 2, 3};
vector<vector<int>> result = permute(nums);
// 输出所有排列
for (int i = 0; i < result.size(); i++) {
for (int j = 0; j < result[i].size(); j++) {
cout << result[i][j] << " ";
}
cout << endl;
}
return 0;
}
输出结果为:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
在示例代码中,我们使用回溯算法求解全排列问题。首先定义了一个 backtrack
函数,它使用递归方式进行回溯搜索。在每一层的搜索中,我们遍历候选集中的所有元素,并选择一个未使用过的元素,将其添加到当前排列中,并继续递归搜索下一个位置。当排列的长度达到原始数组的长度时,我们将当前排列加入到结果集中。每次搜索完成后,我们撤销当前选择,并将其标记为未使用,然后继续搜索下一个候选项,直到搜索完所有的元素。最后返回所有找到的排列。
三.【操作步骤】
1. 定义问题:明确问题的输入、输出和约束条件。确定问题的定义和目标。
2. 设计数据结构:根据问题的特性,选择适当的数据结构来表示问题的状态和解空间。例如,使用数组、链表、树等来存储问题的状态。
3. 实现搜索函数:编写一个搜索函数,该函数接收当前状态和其他必要的参数,并返回下一步要探索的状态。搜索函数应根据问题的定义和约束条件进行适当的状态转换。
4. 实现回溯函数:编写一个回溯函数,该函数用于在搜索过程中回溯到上一个状态。回溯函数应该撤销前一步的操作,以便重新探索其他可能的路径。
5. 实现终止条件:确定搜索过程的终止条件。这可以是找到了满足问题定义的解,或者超过了一定的时间或深度限制。
6. 执行搜索:从初始状态开始,调用搜索函数进行搜索。在搜索的过程中,根据问题的约束条件和终止条件,进行状态转换和回溯操作。
7. 处理结果:根据搜索的结果,处理和输出满足问题定义的解。如果没有找到解,可以输出相应的提示信息。
需要注意以下几点:
- 确保正确性:在实现过程中,确保搜索和回溯函数的正确性。可以使用调试工具和单元测试来验证算法的正确性。
- 优化性能:对于复杂的问题,可能需要考虑优化搜索算法的性能。可以使用剪枝、启发式搜索等技术来提高搜索效率。
- 处理大规模问题:对于大规模问题,可能需要考虑使用适当的数据结构和算法来降低内存和时间的消耗。
这些步骤是一个一般性的指导,具体的实现过程会因问题的不同而有所差异。在实际应用中,可以根据具体问题的需求进行适当的调整和修改。
四.【题目讲解】:
1:自然数的拆分
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 32931 通过数: 19305
【题目描述】
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14
【输入】
输入n。
【输出】
按字典序输出具体的方案。
【输入样例】
7
【输出样例】
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
【算法思路】:
我们可以使用回溯法来解决这个问题。
回溯法的基本思路是不断尝试不同的拆分方式,然后递归地处理剩下的数。
具体步骤如下:
-
定义一个函数backtracking,参数为当前要拆分的数n和当前拆分方案cur_solution。
-
如果当前拆分方案的和等于n,将其加入到结果集中。
-
否则,从1到n-1遍历所有可能的拆分数:
-
将当前拆分数加入到拆分方案中。
-
调用backtracking函数处理剩下的数(即n-当前拆分数)。
-
从拆分方案中移除当前拆分数,以便尝试其他可能的拆分。
-
-
按字典序输出结果集中的拆分方案。
【代码实现】:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> solutions;
void backtracking(int n, vector<int>& cur_solution) {
// 当前拆分方案的和等于n,将其加入到结果集中
if (n == 0) {
solutions.push_back(cur_solution);
return;
}
// 遍历所有可能的拆分数
for (int i = 1; i <= n-1; i++) {
// 将当前拆分数加入到拆分方案中
cur_solution.push_back(i);
// 递归处理剩下的数
backtracking(n - i, cur_solution);
// 从拆分方案中移除当前拆分数
cur_solution.pop_back();
}
}
int main() {
int n;
cin >> n;
vector<int> cur_solution;
backtracking(n, cur_solution);
sort(solutions.begin(), solutions.end());
// 输出结果
for (const auto& solution : solutions) {
cout << n << "=";
for (int i = 0; i < solution.size(); i++) {
cout << solution[i];
if (i < solution.size() - 1) {
cout << "+";
}
}
cout << endl;
}
return 0;
}
2:八皇后
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 21432 通过数: 13194
【题目描述】
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 × 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
【输入】
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1≤b≤92)。
【输出】
输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。
【输入样例】
2
1
92
【输出样例】
15863724
84136275
【算法思路】
- 使用回溯法解决八皇后问题。
- 定义一个大小为8的数组queens,数组中的每个元素表示第i行皇后所在的列数。
- 定义一个递归函数backtracking来尝试放置皇后,参数为当前处理的行号row。
- 如果row等于8,表示已经找到了一组合法的解,将其转化为整数形式,并判断是否为目标解。
- 否则,从列1到8遍历当前行可放置皇后的位置,如果位置合法,则将该位置加入queens数组,然后递归调用backtracking处理下一行。
- 递归调用结束后,需要回溯,将queens数组当前位置的元素重置为0,以便尝试下一个位置。
- 不断重复步骤4~6,直到找到所有的解或达到目标解的编号b为止。
- 输出对应的皇后串。
【代码实现】:
#include <iostream>
#include <vector>
using namespace std;
vector<int> queens(8); // 存储皇后所在的列数
vector<int> solution; // 存储结果
int count = 0; // 计数变量,记录找到的解的数量
// 判断当前位置(row, col)能否放置皇后
bool isSafe(int row, int col) {
// 检查当前列是否有其他皇后
for (int i = 0; i < row; i++) {
if (queens[i] == col) {
return false;
}
}
// 检查左上方对角线是否有其他皇后
for (int i = row-1, j = col-1; i >= 0 && j >= 0; i--, j--) {
if (queens[i] == j) {
return false;
}
}
// 检查右上方对角线是否有其他皇后
for (int i = row-1, j = col+1; i >= 0 && j < 8; i--, j++) {
if (queens[i] == j) {
return false;
}
}
// 当前位置安全
return true;
}
// 回溯法尝试放置皇后
void backtracking(int row) {
// 找到一组合法解,转化为整数形式并判断是否为目标解
if (row == 8) {
count++;
if (count == b) {
int num = 0;
for (int i = 0; i < 8; i++) {
num = num * 10 + queens[i] + 1;
}
solution.push_back(num);
}
return;
}
// 尝试放置皇后
for (int col = 0; col < 8; col++) {
if (isSafe(row, col)) {
queens[row] = col;
backtracking(row + 1);
// 回溯,重置当前位置
queens[row] = 0;
}
}
}
int main() {
int n;
cin >> n;
while (n--) {
int b;
cin >> b;
// 重置变量
queens.assign(8, 0);
solution.clear();
count = 0;
// 求解
backtracking(0);
// 输出结果
for (int i = 0; i < solution.size(); i++) {
cout << solution[i] << endl;
}
}
return 0;
}
这段代码实现了求解八皇后问题的主要逻辑。在主函数中,首先读入测试数据的组数n,然后进行n次循环,每次循环读入一个正整数b,表示要求解的第b个皇后串。接着调用backtracking函数求解八皇后问题,并将结果输出。
注意,这段代码没有包含输入输出的部分,只实现了求解八皇后问题的核心算法。在实际使用时,需要根据题目要求进行输入输出的处理。
3:单词接龙时间限制: 1000 ms 内存限制: 65536 KB 提交数: 11168 通过数: 6661 【题目描述】单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at和atide间不能相连。 【输入】输入的第一行为一个单独的整数n(n<=20)表示单词数,以下n行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。 【输出】只需输出以此字母开头的最长的“龙”的长度。 【输入样例】5
at
touch
cheat
choose
tact
a 【输出样例】23 |
【算法思路】:
- 我们可以将单词为图的遍历问题。
- 首先构建一个图,每个单词作为一个节点,如果两个单词可以接在一起,则在两个节点之间连接一条边。例如,单词"at"和"touch"可以接在一起,它们的连接方式为"atouch"。
- 接下来,使用深度优先搜索(DFS)或广度优先搜索(BFS)遍历这个图,从给定字母开头的节点开始搜索,找出以该节点为起点的最长路径。
- 在搜索过程中,需要记录已经访问过的节点,以避免重复访问,并限制每个单词最多在路径中出现两次。
- 搜索结束后,找到最长路径的长度,并输出结果。
【代码实现】:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
unordered_map<string, vector<string>> graph; // 存储单词图
unordered_map<string, int> visited; // 记录节点的访问次数
// 判断两个单词是否可以接在一起
bool canConnect(const string& word1, const string& word2) {
int len = word1.length();
return (word1.substr(len-2) != word2.substr(0, 2));
}
// 构建单词图
void buildGraph(const vector<string>& words) {
int n = words.size();
for (int i = 0; i < n; i++) {
graph[words[i]] = vector<string>();
visited[words[i]] = 0;
}
for (int i = 0; i < n-1; i++) {
for (int j = i+1; j < n; j++) {
if (canConnect(words[i], words[j])) {
graph[words[i]].push_back(words[j]);
graph[words[j]].push_back(words[i]);
}
}
}
}
// 深度优先搜索找到以某个单词为起点的最长路径
int DFS(const string& word, int length) {
if (visited[word] == 2) {
return length;
}
visited[word]++;
int maxLength = 0;
for (const string& nextWord : graph[word]) {
if (visited[nextWord] < 2) {
int len = DFS(nextWord, length + nextWord.length() - 2);
maxLength = max(maxLength, len);
}
}
visited[word]--;
return maxLength;
}
int main() {
int n;
cin >> n;
vector<string> words(n);
for (int i = 0; i < n; i++) {
cin >> words[i];
}
string start;
cin >> start;
buildGraph(words);
int maxLength = 0;
for (const string& word : words) {
if (word[0] == start[0]) {
maxLength = max(maxLength, DFS(word, word.length() - 2));
}
}
cout << maxLength << endl;
return 0;
}
4:【例5.2】组合的输出
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 45855 通过数: 22910
【题目描述】
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你用递归的方法输出所有组合。
例如n=5,r=3,所有组合为:
1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
【输入】
一行两个自然数n、r(1<n<21,1≤r≤n)。
【输出】
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。
【输入样例】
5 3
【输出样例】
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
【算法思路】:
- 可以使用递归的方法输出所有组合。
- 递归的终止条件是当已经选取的元素数量等于需要选取的数量时,输出当前组合。
- 递归的过程是,对于当前可选元素的范围,依次选择一个元素加入组合中,然后递归调用函数选择剩下的元素,直到选择的元素数量达到需要选择的数量。
- 在递归调用之前,将已经选择的元素放入当前组合中,并传递已选择的元素的数量作为参数。
- 在递归调用之后,从当前组合中移除最后一个加入的元素,以便进行下一轮的选择。
【代码实现】:
#include <iostream>
#include <vector>
using namespace std;
void combination(vector<int>& nums, vector<int>& currComb, int startPos, int count) {
if (count == 0) { // 终止条件:已经选取了需要选择的数量
for (int num : currComb) {
cout << num << " ";
}
cout << endl;
return;
}
for (int i = startPos; i < nums.size(); i++) { // 选择当前元素,并递归选择剩下的元素
currComb.push_back(nums[i]);
combination(nums, currComb, i+1, count-1);
currComb.pop_back();
}
}
int main() {
int n, r;
cin >> n >> r;
vector<int> nums(n);
for (int i = 0; i < n; i++) {
nums[i] = i + 1; // 将自然数1~n存入数组
}
vector<int> currComb;
combination(nums, currComb, 0, r);
return 0;
}
5:红与黑
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 37425 通过数: 15413
【题目描述】
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
【输入】
包括多组数据。每组数据的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下:
1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每组数据中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
【输出】
对每组数据,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
【输入样例】
6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0
【输出样例】
45
【算法思路】:
- 首先找到初始位置的坐标。
- 使用深度优先搜索(DFS)算法来计算从初始位置出发能到达的瓷砖数。
- 在深度优先搜索过程中,对于每个相邻的黑色瓷砖,进行递归调用,同时将当前瓷砖标记为已访问。
- 根据题目要求,每个相邻的瓷砖只能是上下左右四个方向之一,因此使用一个方向数组 dx[] 和 dy[] 来分别表示下一步在 x 轴和 y 轴的偏移量。
- 在递归调用之前,将当前瓷砖标记为已访问,即将其改变为红色。
- 在递归调用之后,将当前瓷砖标记为未访问,即将其改变为黑色。
- 统计访问过的黑色瓷砖数量。
- 搜索结束后,输出访问过的黑色瓷砖数量。
【算法流程】:
- 读入矩阵的宽度w和高度h。
- 如果w和h都为0,表示输入结束,退出循环。
- 初始化一个二维字符数组grid用于存储瓷砖的颜色。
- 初始化一个二维布尔数组visited用于标记瓷砖是否访问过。
- 在读入每个瓷砖的颜色时,如果颜色为'@',表示初始位置,记录初始位置的坐标。
- 调用dfs函数计算从初始位置出发能到达的瓷砖数,并输出结果。
- 重复步骤2-6,直到输入结束。
其中dfs函数使用深度优先搜索算法来计算从初始位置出发能到达的瓷砖数。对于每个相邻的黑色瓷砖,进行递归调用,并将当前瓷砖标记为已访问。搜索结束后返回访问过的黑色瓷砖数量。
【代码实现】:
#include <iostream>
#include <vector>
using namespace std;
int dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] == '#' || visited[x][y]) {
return 0;
}
visited[x][y] = true;
int dx[] = {0, 0, -1, 1};
int dy[] = {-1, 1, 0, 0};
int res = 1;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
res += dfs(grid, visited, nx, ny);
}
return res;
}
int main() {
int w, h;
while (cin >> w >> h) {
if (w == 0 && h == 0) {
break;
}
vector<vector<char>> grid(h, vector<char>(w));
vector<vector<bool>> visited(h, vector<bool>(w, false));
int startX, startY;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
cin >> grid[i][j];
if (grid[i][j] == '@') {
startX = i;
startY = j;
}
}
}
int ans = dfs(grid, visited, startX, startY);
cout << ans << endl;
}
return 0;
}
创作不易,点个👍,观个注吧。