搜索问题
搜索问题本质也是暴力枚举,一般想到暴力也要想到利用回溯枚举。
排序和组合问题
回溯问题
图论中的搜索问题
- 与一般的搜索问题一致,只不过要多定义方向
DFS和BFS的分析
DFS
BFS
-
BFS适合搜索最短路径,时间效率与边长和顶点数有关
-
一般适用于大多数搜索方法的数量级
-
BFS也可以用于搜索所有路径,但是代码比较难写!
P1219 [USACO1.5] 八皇后 Checker Challenge
题目描述
一个如下的 6 × 6 6 \times 6 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5 来描述,第 i i i 个数字表示在第 i i i 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6 1\ 2\ 3\ 4\ 5\ 6 1 2 3 4 5 6
列号 2 4 6 1 3 5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前
3
3
3 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n n n,表示棋盘是 n × n n \times n n×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例 #1
输入 #1
6
输出 #1
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
说明/提示
【数据范围】
对于
100
%
100\%
100% 的数据,
6
≤
n
≤
13
6 \le n \le 13
6≤n≤13。
题目翻译来自NOCOW。
USACO Training Section 1.5
#define MAX_SIZE 1000009
using ll = long long;
using namespace std;
int n,ans=0;
vector<vector<int>>graph(20,vector<int>(20,0));
vector<int>temp;
bool isvalid(int h,int w) {
for (int i = 1; i <= n; i++) {
if (graph[i][w]) {
return false;
}
}
//bottom
int bottom_upper = min(n - h, n - w);//h=2,w=1
int bottom_lower = min(h - 1, w - 1);
for (int i = 1; i<= bottom_upper; i++) {
if (graph[h + i][w + i]) {
return false;
}
}
for (int i = 1; i <= bottom_lower; i++) {
if (graph[h - i][w - i]) {
return false;
}
}
//top
int top_upper = min(h - 1, n - w);
int top_lower = min(w - 1, n - h);
for (int i = 1; i <= top_upper;i++) {
if (graph[h - i][w + i]) {
return false;
}
}
for (int i = 1; i <= top_lower; i++) {
if (graph[h + i][w - i]) {
return false;
}
}
return true;
}
void dfs(int depth) {
if (depth > n) {
if (ans < 3) {
for (auto item : temp) {
cout << item << " ";
}
cout << endl;
}
ans++;
return;
}
for (int i = 1; i <= n; i++) {
if (isvalid(depth, i)) {
temp.push_back(i);
graph[depth][i] = 1;
dfs(depth + 1);
graph[depth][i] = 0;
temp.pop_back();
}
}
}
void solve() {
cin >> n;
dfs(1);
cout << ans;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}
P1443 马的遍历
题目描述
有一个 n × m n \times m n×m 的棋盘,在某个点 ( x , y ) (x, y) (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式
输入只有一行四个整数,分别为 n , m , x , y n, m, x, y n,m,x,y。
输出格式
一个 n × m n \times m n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 − 1 -1 −1)。
输入输出样例 #1
输入 #1
3 3 1 1
输出 #1
0 3 2
3 -1 1
2 1 4
说明/提示
数据规模与约定
对于全部的测试点,保证 1 ≤ x ≤ n ≤ 400 1 \leq x \leq n \leq 400 1≤x≤n≤400, 1 ≤ y ≤ m ≤ 400 1 \leq y \leq m \leq 400 1≤y≤m≤400。
DFS
- 优点:它会探索所有的路径
- 缺点:效率比较低
- 一般搜索使用BFS比较多
# include<bits/stdc++.h>
#define MAX_VALUE 1000009
using ll = long long;
using namespace std;
int n, m, x, y;
int dir[8][2] = { 2,1, 1,2, -1,2, -2,1, -1,-2, -2,-1, 1,-2,2,-1 };
vector<vector<bool>>visited(500, vector<bool>(500, 0));
vector<vector<int>>graph(500, vector<int>(500, MAX_VALUE));
void dfs(int x, int y,int dist) {
//cout << "x==" << x << " y==" << y << endl;
graph[x][y] = min(graph[x][y], dist);
for (int i = 0; i < 8; i++) {
int next_x = x + dir[i][0];
int next_y = y + dir[i][1];
if (next_x>=1 && next_x<=n && next_y>=1 && next_y<=m && !visited[next_x][next_y]) {
visited[next_x][next_y] = true;
dfs(next_x, next_y,dist+1);
visited[next_x][next_y] = false;
}
}
}
void solve() {
cin >> n >> m >> x >> y;
dfs(x,y,0);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (graph[i][j] == MAX_VALUE) {
cout << -1 << " ";
}
else {
cout << graph[i][j] << " ";
}
}
cout << endl;
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}
BFS
- BFS:搜索效率比较高
- 缺点:没有探索全部路径
- 如果需要探索全部路径,需要在
point
结构体数组中保留visited
作为局部变量
# include<bits/stdc++.h>
#define MAX_VALUE 1000009
using ll = long long;
using namespace std;
int n, m, x, y;
int dir[8][2] = { 2,1, 1,2, -1,2, -2,1, -1,-2, -2,-1, 1,-2,2,-1 };
vector<vector<int>>graph(500, vector<int>(500, MAX_VALUE));
vector<vector<bool>>visited(500,vector<bool>(500,false));
typedef struct point {
int x, y,dist;
point(int a, int b,int c) :x(a), y(b),dist(c) {};
}point;
queue<point>q;
void bfs() {
q.push(point(x, y,0));
while (!q.empty()) {
point cur = q.front();
graph[cur.x][cur.y] = min(cur.dist, graph[cur.x][cur.y]);
q.pop();
for (int i = 0; i < 8; i++) {
int next_x = cur.x + dir[i][0];
int next_y = cur.y + dir[i][1];
if (next_x >= 1 && next_x <= n && next_y >= 1 && next_y <= m
&& !visited[next_x][next_y]) {
point p(next_x, next_y,cur.dist+1);
visited[next_x][next_y] = true;
q.push(p);
}
}
}
}
void solve() {
cin >> n >> m >> x >> y;
bfs();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (graph[i][j] == MAX_VALUE) {
cout << -1 << " ";
}
else {
cout << graph[i][j] << " ";
}
}
cout << endl;
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}
P1135 奇怪的电梯
题目背景
感谢 @yummy 提供的一些数据。
题目描述
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i i i 层楼( 1 ≤ i ≤ N 1 \le i \le N 1≤i≤N)上有一个数字 K i K_i Ki( 0 ≤ K i ≤ N 0 \le K_i \le N 0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3 , 3 , 1 , 2 , 5 3, 3, 1, 2, 5 3,3,1,2,5 代表了 K i K_i Ki( K 1 = 3 K_1=3 K1=3, K 2 = 3 K_2=3 K2=3,……),从 1 1 1 楼开始。在 1 1 1 楼,按“上”可以到 4 4 4 楼,按“下”是不起作用的,因为没有 − 2 -2 −2 楼。那么,从 A A A 楼到 B B B 楼至少要按几次按钮呢?
输入格式
共二行。
第一行为三个用空格隔开的正整数,表示 N , A , B N, A, B N,A,B( 1 ≤ N ≤ 200 1 \le N \le 200 1≤N≤200, 1 ≤ A , B ≤ N 1 \le A, B \le N 1≤A,B≤N)。
第二行为 N N N 个用空格隔开的非负整数,表示 K i K_i Ki。
输出格式
一行,即最少按键次数,若无法到达,则输出 -1
。
输入输出样例 #1
输入 #1
5 1 5
3 3 1 2 5
输出 #1
3
说明/提示
对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 200 1 \le N \le 200 1≤N≤200, 1 ≤ A , B ≤ N 1 \le A, B \le N 1≤A,B≤N, 0 ≤ K i ≤ N 0 \le K_i \le N 0≤Ki≤N。
本题共 16 16 16 个测试点,前 15 15 15 个每个测试点 6 6 6 分,最后一个测试点 10 10 10 分。
DFS
#include <bits/stdc++.h>
#define MAX_VALUE 1000009
using ll = long long;
using namespace std;
int n, a, b,ans=MAX_VALUE;
vector<int>v(209,0);
vector<bool>visited(209, false);
void dfs(int st,int step=0) {
//cout << "st==" << st << endl;
if (st == b) {
ans = min(ans, step);
return;
}
if (st + v[st] <= n && !visited[st+v[st]]) {
visited[st + v[st]] = true;
dfs(st + v[st], step + 1);
visited[st + v[st]] = false;
}
if (st -v[st] >= 1 && !visited[st - v[st]]) {
visited[st - v[st]] = true;
dfs(st - v[st], step + 1);
visited[st - v[st]] = false;
}
}
void solve() {
cin >> n >> a>>b;
for (int i = 1; i <= n; i++) {
cin >> v[i];
}
dfs(a);
cout << (ans==MAX_VALUE?-1:ans);
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}
BFS
#include <bits/stdc++.h>
#define MAX_VALUE 1000009
using ll = long long;
using namespace std;
int n, a, b,ans=MAX_VALUE;
vector<int>v(209, 0);
vector<bool>visited(209, false);
typedef struct node {
int fr, step;
node(int x, int y) :fr(x), step(y) {};
}node;
queue<node>q;
void bfs(int st) {
q.push(node(st, 0));
while (!q.empty()) {
node cur=q.front();
q.pop();
//终止条件
if (cur.fr == b) {
ans = min(ans, cur.step);
}
if (cur.fr + v[cur.fr] <= n && !visited[cur.fr + v[cur.fr]]) {
q.push(node(cur.fr + v[cur.fr], cur.step + 1));
visited[cur.fr + v[cur.fr]] = true;
}
if (cur.fr - v[cur.fr] >= 1 && !visited[cur.fr - v[cur.fr]]) {
q.push(node(cur.fr - v[cur.fr], cur.step + 1));
visited[cur.fr - v[cur.fr]] = true;
}
}
}
void solve() {
cin >> n >> a>>b;
for (int i = 1; i <= n; i++) {
cin >> v[i];
}
bfs(a);
cout << (ans==MAX_VALUE?-1:ans);
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}