搜索
1 递归
1.1.什么是递归,它是如何工作的?
我们先来看一下递归(recursion)的定义:
递归是一种解决问题的有效方法,在递归过程中,函数将自身作为子例程调用。
简单说程序调用自身的编程技巧叫递归。递归的思想是把一个大型复杂问题层层转化为一个与原问题规模更小的问题,问题被拆解成子问题后,递归调用继续进行,直到子问题无需进一步递归就可以解决的地步为止。
使用递归需要避免出现死循环,为了确保递归正确工作,递归程序应该包含2个属性:
- 基本情况(bottom cases),基本情况用于保证程序调用及时返回,不在继续递归,保证了程序可终止。
- 递推关系(recurrentce relation),可将所有其他情况拆分到基本案例
一说起递归,我想每个人都不陌生。举个从小就听过的例子:从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,从前有座山...
在使用递归时,需要注意定义一个从函数退出的条件,否则会进入死循环
2 深度优先搜索(Deep First Search)
深度优先搜索算法(Depth First Search)一种用于遍历或搜索树或图的算法。沿着树的深度遍历图的节点,尽可能深的搜索图的分支。当节点\(v\)的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点\(v\)的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为\(O(n!)\)。
例题理解
1.洛谷 P1683 入门
#include <bits/stdc++.h>
using namesapce std;
int n, m;
char g[25][25];
bool st[25][25];
int res;
void dfs(int x, int y) {
if (x < 0 || y < 0 || x > n || y > m || !st[x][y] || g[x][y] == '#') return ;
res ++;
st[x][y] = false;
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y + 1);
dfs(x, y - 1);
}
int main() {
cin >> m >> n;
int x, y;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> g[i][j];
if (g[i][j] == '@') x = i, y = j;
st[i][j] = true;
}
dfs(x, y);
cout << res << endl;
}
2.洛谷 P1162 填土颜色:
#include <iostream>
using namespace std;
int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
int n;
int a[35][35], b[35][35];
void dfs(int x, int y) {
if (x < 0 || y < 0 || x > n + 1 || y > n + 1 || a[x][y])
return;
a[x][y] = 1;
for (int i = 0; i < 4; i++)
dfs(x + dx[i], y + dy[i]);
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
cin >> b[i][j];
if (b[i][j] == 0) a[i][j] = 0;
else a[i][j] = 2;
}
for (int i = 1; i <= n; i++) {
if (!b[1][i]) dfs(1, i);
if (!b[n][i]) dfs(n, i);
if (!b[i][1]) dfs(i, 1);
if (!b[i][n]) dfs(i, n);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
std::cout << (a[i][j] + 2) % 3 << " ";
}
cout << endl;
}
return 0;
}
做一个形象的比喻,dfs好比走迷宫,得一直走到头,看看路的尽头是不是出口,如果是,就直接走出去,如果不是,那就返回上一个“标记点”寻找不一样的可行方法。dfs的实现关键在于回溯,这个可以用两种方法实现(递归、堆栈)
例题理解
洛谷 P1157 组合的输出:
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<int> v;
void dfs(int u, int state) {
if (u - state + n < m) return ;
if (u == m + 1) {
for (auto x : v) printf("%3d", x);
cout << "\n";
return ;
}
for (int i = state; i <= n; i ++ ) {
v.push_back(i);
dfs(u + 1, i + 1);
v.pop_back();
}
}
int main() {
cin >> n >> m;
dfs(1, 1);
return 0;
}
3广度优先搜索(Breath First Search)
广度优先搜索(Breath First Search)属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。因为所有节点都必须被储存,因此BFS的空间复杂度为 \(O(V + E)\),其中\(V\)是节点的数目,而\(E\)是图中边的数目。最差情形下,时间复杂度为 \(O(V + E)\),其中\(V\)是节点的数目,而\(E\)是图中边的数目。
例题理解
1.洛谷 P1746 离开中山路
#include <bits/stdc++.h>
using namespace std;
int n;
char g[N][N];
int vis[N][N];
bool st[N][N];
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
int bfs(int sx, int sy, int ex, int ey) {
queue<pair<int, int>> q;
q.push({sx, sy});
st[sx][sy] = true;
vis[sx][sy] = 0;
while (q.size()) {
pair<int, int> t = q.front();
q.pop();
int x = t.first, y = t.second;
for (int i = 0; i < 4; i ++ ) {
int xx = x + dx[i], yy = y + dy[i];
if (xx >= 1 && yy >= 1 && xx <= n && yy <= n && !st[xx][yy] && g[xx][yy] == '0') {
st[xx][yy] = true;
vis[xx][yy] = vis[x][y] + 1;
q.push({xx, yy});
}
if (xx == ex && yy == ey) return vis[ex][ey];
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
cin >> g[i][j];
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << bfs(x1, y1, x2, y2) << "\n";
return 0;
}
2.洛谷 P1332 血色先锋队
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m, a, b;
pair<int, int> v1[N], v2[N];
bool st[510][510];
int vis[510][510];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
queue<pair<int,int> > q;
void bfs() {
while (q.size()) {
pair<int, int> t = q.front();
q.pop();
int x = t.first, y = t.second;
st[x][y] = true;
for (int i = 0; i < 4; i ++ ) {
int xx = x + dx[i], yy = y + dy[i];
if (xx >= 1 && yy >= 1 && xx <= n && yy <= m && !st[xx][yy]){
vis[xx][yy] = min(vis[x][y] + 1, vis[xx][yy]);
st[xx][yy] = true;
q.push({xx, yy});
}
}
}
}
void solve() {
memset(vis, 0x3f, sizeof vis);
cin >> n >> m >> a >> b;
for (int i = 0; i < a; i++ ) {
int x, y;
cin >> x >> y;
v1[i] = {x, y};
q.push({x,y});
vis[x][y] = 0;
}
for (int i = 0; i < b; i++ ) {
int x, y;
cin >> x >> y;
v2[i] = {x, y};
}
bfs();
for (int i = 0; i < b; i ++ ) cout << vis[v2[i].first][v2[i].second] << "\n";
}
int main() {
solve();
}
#include <bits/stdc++.h>
using i64 = long long;
#define rep(i,a,b) for (int i = a; i < b; i++)
#define per(i,a,b) for (int i = a; i >= b; i--)
#define SZ(s) int(s.size())
#define all(v) v.begin(), v.end()
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
std::vector<std::array<int, 2>> stp;
for (int i = -sqrt(m) - 1; i <= sqrt(m) + 1; i++) {
for (int j = -sqrt(m) - 1; j <= sqrt(m) + 1; j++) {
if (i * i + j * j == m) {
stp.push_back({i, j});
}
}
}
std::queue<std::array<int, 2>> q;
std::vector<std::vector<int>> dist(n + 1, std::vector<int> (n + 1, -1));
dist[1][1] = 0;
q.push({1, 1});
while(q.size()) {
auto t = q.front();
q.pop();
for (auto& p : stp) {
int x = t[0] + p[0], y = t[1] + p[1];
if (x > n || y > n || x < 1 || y < 1 || ~dist[x][y]) continue;
dist[x][y] = dist[t[0]][t[1]] + 1;
q.push({x, y});
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
std::cout << dist[i][j] << " \n"[j == n];
}
}
return 0 ^ 0;
}