搜索算法 (一)- 深度优先和广度优先
1 搜索算法简介
1.1 算大简介
搜索算法通常有深度优先搜索 (dfs) 和广度优先搜索 (bfs) 两种类型。dfs 是纵深搜索,将一条路搜到底,再尝试第二条路。而广度优先追求搜索的广度,即每一条路都向前搜索一步之后,再搜索第二步。
两种算法各有优劣
- dfs 空间效率较高、适合回溯、可以检测环等
- bfs 适合找最短路径、层级遍历、查找最短跳数等
当然也有这两种算法的改进算法、比方说迭代加深搜索、双向广度优先搜索等,但这些都是在这两种算法的基础上进行的。
1.2 算法思路
dfs 通常使用递归进行,从某点开始,递归搜索下一个可行的点,直到搜索到答案
void dfs(int u){vis[u] = 1;for(int v : next[u]){if(!vis[v]) dfs(v);// to do something}
}
bfs 通常借助队列进行,从起点开始,逐步搜索
void bfs(int s){q.push(s);while(!q.empty()){int u = q.front();q.pop();for(int v : next[u]){// to do somethingif(!vis[v]){q.push(v);}}}
}
2 例题
2.1
P1219 [USACO1.5] 八皇后 Checker Challenge
题目大意:在一个 n x n (n <= 13) 的网格里放置 n 个棋子(皇后),要求它们不能同行、同列、同斜线,求可能的方法数量。
思路:直接从逐行尝试,只要是和之前放置的不冲突就一直放到最后,有冲突就换一条路继续尝试,直到所有的方式都尝试过即可,所以本质就是穷举法。这种方法叫做回溯法,因为就是不断试错,不断回溯,不适合大数据范围。
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;
int n;int a[14]; // a[i]:第 i 行的皇后放的位置
int s = 0;bool is_ok(int i, int k) { // 检查冲突for (int j = 1; j < i; j++) {if (a[j] == k || i - j == abs(k - a[j]))return false;}return true;
}void f(int i) { // dfs 深度搜索if (i > n) {if(s < 3) {for (int k = 1; k <= n; k++) {cout << a[k] << " ";}cout << endl;}s++;return;}for (int k = 1; k <= n; k++) {if (is_ok(i, k)) {a[i] = k;f(i + 1);}}
}int main() {cin >> n;f(1);cout << s;return 0;
}
即使数据量很小,但 n = 13 时仍然超时,所以可以进行改进。
将检查冲突的部分,直接用数组记录下来,记录每一行、每一列、每条左斜线、每条有些线是否放置了皇后,而不用去搜索。
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"
#include "time.h"using namespace std;
int n;int a[14]; // 存放的位置
int b[14]; // 记录该列有没有存数
int c[30]; // 记录该对角线有没有存放数字
int d[30]; // 记录该对角线有没有存放数字int s = 0;void f(int i) {if (i > n) {if (s < 3) {for (int k = 1; k <= n; k++) {cout << a[k] << " ";}cout << endl;}s++;return;}for (int k = 1; k <= n; k++) {if (b[k] == 0 && c[i + k] == 0 && d[n - i + k] == 0) {a[i] = k;b[k] = 1, c[i + k] = 1, d[n - i + k] = 1;f(i + 1);b[k] = 0, c[i + k] = 0, d[n - i + k] = 0;}}
}int main() {cin >> n;f(1);cout << s;return 0;
}
2.2
P2392 kkksc03考前临时抱佛脚
题目大意:四组数据,每组数据分成两份,使得两组和最大值最小
思路:对于每组数据 dfs 尝试所有分组方式,找到最接近平分的那种
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;const int N = 20;
int a[4][20];
int s[4];
int n[4];int min_v;void f(int i, int m, int ss) {if (m == n[i]) {min_v = min(max(s[i] - ss, ss), min_v);return;}f(i, m + 1, ss);f(i, m + 1, ss + a[i][m]);
}int main() {int res = 0;for (int &i : n) cin >> i;for (int i = 0; i < 4; i++) for (int j = 0; j < n[i]; j++)cin >> a[i][j], s[i] += a[i][j];for (int i = 0; i < 4; i++) {min_v = INT32_MAX;f(i, 0, 0);//cout << min_v << endl;res += min_v;}cout << res;return 0;
}
2.3
P1443 马的遍历
题目大意:一个棋盘里(中国象棋)有一个马,问他跳到每一个格子需要几步
思路: bfs,从起点开始,搜索一步可到的位置,在从新搜索出来的位置上搜索两步可到的位置、以此类推。
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;const int N = 4e2 + 1;int a[N][N];
int dx[] = {1, 1, -1, -1, 2, 2, -2, -2};
int dy[] = {2, -2, 2, -2, -1, 1, -1, 1};int main() {int m, n, x, y;cin >> m >> n >> x >> y;memset(a, -1, sizeof(a));a[x][y] = 0;queue<int> qx, qqx;queue<int> qy, qqy;qx.push(x);qy.push(y);int xx, yy;int step = 1;while (true) {while (!qx.empty()) {int x1 = qx.front();qx.pop();int y1 = qy.front();qy.pop();for (int i = 0; i < 8; i++) {xx = x1 + dx[i];yy = y1 + dy[i];if (xx <= m && xx >= 1 && yy >= 1 && yy <= n) {if (a[xx][yy] == -1) {a[xx][yy] = step;//cout << xx << " " << yy << " " << step << endl;qqx.push(xx);qqy.push(yy);}}}}step++;qx = qqx;qy = qqy;qqx = queue<int>();qqy = queue<int>();if (qx.empty()) break;}for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++)cout << a[i][j] << " ";cout << endl;}return 0;
}
2.4
P1135 奇怪的电梯
题目大意:有一个电梯,每一层楼 u 都有一个数字 x 表示,该层只能上 x 楼即到 u + x 楼, 或者下 x 楼到 u - x 楼,问从 A 到 B 最少需要走几次电梯
思路,从起点开始,搜索一步到的楼层、两步到的楼层、以此类推,直到 B 楼出现
#include <sstream>
#include "iostream"
#include "algorithm"
#include "vector"
#include "unordered_map"
#include "unordered_set"
#include "queue"using namespace std;const int N = 201;
int K[N], a[N];int main() {int n, A, B;cin >> n >> A >> B;for (int i = 1; i <= n; i++) cin >> K[i];queue<int> q;q.push(A);a[A] = 1;while (!q.empty()) {int c = q.front();if (c == B) {cout << a[c] - 1;return 0;}q.pop();int x = c + K[c];if (x <= n && !a[x]) {q.push(x);a[x] = a[c] + 1;}int y = c - K[c];if (y >= 1 && !a[y]) {q.push(y);a[y] = a[c] + 1;}}cout << "-1";return 0;
}
2.5
P2895 [USACO08FEB] Meteor Shower S
题目大意:有一些流星会在某个时刻(不是同时的)落在一些格子上,导致有些格子(在落下之后)不能走,求到达某一个安全的格子上的最小时间。
思路:先记录哪些格子是安全的,然后按照时间进行 bfs 并按照数据加入流星砸过的格子
#include "iostream"
#include "math.h"
#include "algorithm"
#include "queue"
#include "vector"using namespace std;
const int N = 302;
const int MC = 5e4;
int M;
bool a[N][N]; // 记录当前时刻 (i, j) 是否安全
bool b[N][N]; // 记录(i, j)是否安全
bool c[N][N]; // 记录(i, j)有没有走过struct LX { //(x, y)t 时刻流星坠落的位置int x, y, t;
};LX lx[MC];bool comp(LX &aa, LX &bb) {return aa.t < bb.t;
}int k = 0;queue<vector<int>> pos;
queue<vector<int>> pos2;int dr[] = {0, 1, -1, 0};
int dc[] = {1, 0, 0, -1};void f(int t) {while (lx[k].t == t) { // t 时刻所有的流星a[lx[k].x][lx[k].y] = true; // 相邻的位置不能走if (lx[k].y > 0) a[lx[k].x][lx[k].y - 1] = true;a[lx[k].x][lx[k].y + 1] = true;if (lx[k].x > 0) a[lx[k].x - 1][lx[k].y] = true;a[lx[k].x + 1][lx[k].y] = true;k++;}pos2 = queue<vector<int>>();while (!pos.empty()) {auto xx = pos.front();pos.pop();for (int i = 0; i < 4; i++) { //四个方向走一下if (a[xx[0]][xx[1]]) continue;int r1 = xx[0] + dr[i];int c1 = xx[1] + dc[i];if (r1 < 0 || c1 < 0) continue;if (!b[r1][c1]) { // 如果到了安全的地方cout << t + 1;exit(0);}if (!a[r1][c1]) { // 如果安全if (!c[r1][c1]) { // 如果没走过pos2.push({r1, c1});c[r1][c1] = true;}}}}pos = pos2;if (pos.empty()) {cout << -1;exit(0);}f(t + 1); // 下一个时刻
}int main() {cin >> M;for (int i = 0; i < M; i++) {cin >> lx[i].x >> lx[i].y >> lx[i].t; // (x, y)t 时刻流星坠落的位置b[lx[i].x][lx[i].y] = true; // 相邻的位置都不能用了if (lx[i].y > 0) b[lx[i].x][lx[i].y - 1] = true;b[lx[i].x][lx[i].y + 1] = true;if (lx[i].x > 0) b[lx[i].x - 1][lx[i].y] = true;b[lx[i].x + 1][lx[i].y] = true;}sort(lx, lx + M, comp); // 按照时间排序pos.push({0, 0}); // 开始c[0][0] = 1; f(0);return 0;
}
2.5
P1036 [NOIP 2002 普及组] 选数
题目大意:对于一些整数,找出所有可能组合数满足和为质数
思路:dfs 搜索每一种可能
#include <vector>
#include <math.h>
#include "iostream"
#include "unordered_map"using namespace std;void getSum(int k, int *nums, int n, unordered_map<int, int> &map, int sum, int pos) {if (k == 0) {if (map.find(sum) == map.end()) {map[sum] = 1;} else {map[sum]++;}return;}if (k > n - pos)return;getSum(k - 1, nums, n, map, sum + nums[pos], pos + 1);getSum(k, nums, n, map, sum, pos + 1);}bool isPrime(int n) {if (n < 2)return false;if (n == 2)return true;for (int i = 2; i <= sqrt(n); i++) {if (n % i == 0)return false;}return true;
}int main() {unordered_map<int, int> map;int k, n;cin >> n >> k;int nums[n];for (int i = 0; i < n; i++)cin >> nums[i];getSum(k, nums, n, map, 0, 0);int count = 0;for (auto x:map) {if (isPrime(x.first)) {count += x.second;}}cout << count << endl;return 0;
}
2.6
P2036 [COCI 2008/2009 #2] PERKET
题目大意:n 中食材,每个食材的酸度为 sis_isi 和 bib_ibi 找一种配方至少一种食材,使得酸度和苦度最均衡。
思路:dfs 尝试所有组合
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;const int N = 10;int s[N], b[N];int m = 1E9;void f(int n, int d, int e, bool ok) {if (n == 0) {if (ok) m = min(abs(d - e), m);d *= s[0], e += b[0], m = min(abs(d - e), m);return;}f(n - 1, d, e, ok);d *= s[n], e += b[n];f(n - 1, d, e, 1);
}int main() {int n;cin >> n;for (int i = 0; i < n; i++) {cin >> s[i] >> b[i];}f(n-1,1,0,0);cout << m;return 0;
}
2.7
P1605 迷宫
题目大意:N×M 方格的迷宫,起点到终点不走重复格子的路径有几条
思路:回溯法,标记走过的格子,回溯时及时擦出标记
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#include<utility>
#include "unordered_map"
#include "unordered_set"using namespace std;
const int N = 6;int a[N][N]; // 迷宫 0 没有障碍, 1 有障碍
bool vis[N][N]; // vis[i][j] = 0 (i, j) 处没有走过int n, m, sx, sy, ex, ey, t;
int ans;int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};void dfs(int x, int y) {if (x == ex && y == ey) {ans++;return;}vis[x][y] = 1; //表示此处走过了for (int i = 0; i < 4; i++) {int xx = x + dx[i];int yy = y + dy[i];if (xx > 0 && xx <= n && yy > 0 && yy <= m && !a[xx][yy] && !vis[xx][yy])//没有出界 没有障碍 没有走过dfs(xx, yy);}vis[x][y] = 0;
}int main() {cin >> n >> m >> t;cin >> sx >> sy >> ex >> ey;int x, y;for (int i = 0; i < t; i++) {cin >> x >> y;a[x][y] = 1;}dfs(sx, sy);cout << ans;
}
2.8
P1019 [NOIP 2000 提高组] 单词接龙(疑似错题)
题目大意:给定一些单词,给出单词接龙的最长长度,每个单词最多用两次
思路:将单词之间的连接关系表示成有向边,用 dfs 搜索接龙的长度
#include <sstream>
#include "iostream"
#include "algorithm"
#include "vector"using namespace std;
const int N = 21;
string a[N];int n;
vector<int> starts;
vector<vector<int>> m[N];
int vis[N];
int ans = 0;int f(int i, int s) {ans = max(ans, s);for (int j = 0; j < m[i].size(); j++) {vector<int> xx = m[i][j];if (vis[xx[0]] < 2) {vis[xx[0]]++;f(xx[0], s + a[xx[0]].size() - xx[1]);vis[xx[0]]--;}}
}int main() {char c;cin >> n;for (int i = 0; i < n; i++) cin >> a[i];cin >> c;for (int i = 0; i < n; i++) {if (a[i][0] == c) {starts.push_back(i); //开始单词}}for (int i = 0; i < n; i++) {// m[i] 第 i 个单词可以连接的下一个单词的编号for (int j = 0; j < n; j++) {//if (j == i) continue;int k = a[i].size();int k2 = a[j].size();for (int t = 1; t <= min(k, k2) - 1; t++) {if (a[i].substr(k - t) == a[j].substr(0, t)) {m[i].push_back({j, t});break;}}}}for (int i = 0; i < starts.size(); i++) {vis[starts[i]]++;f(starts[i], a[starts[i]].size());vis[starts[i]]--;}cout << ans;return 0;
}
2.8
P1101 单词方阵
题目大意:给出一个字母矩阵,搜索里面 “yizhong” 的个数
思路:dfs 回溯,从每个格子开始,往 8 个方向搜索
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#include<utility>
#include "unordered_map"
#include "unordered_set"using namespace std;
const int N = 101;
const string s = "yizhong";
char a[N][N];
bool b[N][N];
int n;int dx[] = {1, -1, 0, 0, 1, 1, -1, -1};
int dy[] = {0, 0, 1, -1, -1, 1, -1, 1};void dfs(int x, int y, int k, int d) {if (k == 7) {int xx = x;int yy = y;for (int i = 0; i < 7; i++) {xx -= dx[d];yy -= dy[d];b[xx][yy] = 1;}return;}if (a[x][y] != s[k])return;dfs(x + dx[d], y + dy[d], k + 1, d);
}int main() {cin >> n;char x;for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)cin >> a[i][j];for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)for (int k = 0; k < 8; k++) {if (i + 6 * dx[k] >= 0 && i + 6 * dx[k] < n && j + 6 * dy[k] >= 0 && j + 6 * dy[k] < n) {dfs(i, j, 0, k);}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (b[i][j]) cout << a[i][j];else cout << '*';}cout << endl;}}
2.9
P2404 自然数的拆分问题
题目大意:将正整数拆解成多个正整数相加的形式
思路:
如果函数 f 将 n 拆解成最大加数不超过 m 的形式,则函数 f 可以写成递归形式即 f(n, m) = f(n, m-1) + f(n-m,m)
即 n 拆解成 不大于 m 的数相加的情况分为两种
没有 等于 m 的,f(n, m-1)
有等于 m 的 f(n-m,m)
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;vector<string> res;void f(int n, int k, string s) { // 分解 n // 最大分解不超过 k//if (k > n) k = n;if (k == 1) {string ss;for (int i = 0; i < n; i++) ss += "1+";res.push_back(ss + s);return;}if (n == 1) {res.push_back("1+" + s);return;}for (int t = 1; t <= k; t++) {if (n - t > 0)f(n - t, t, to_string(t) + "+" + s);}if (n <= k) res.push_back(to_string(n) + "+" + s);
}int main() {int n;cin >> n;f(n, n, "");sort(res.begin(), res.end());for (int i = 0; i < res.size() - 1; i++) {cout << res[i].substr(0, res[i].size() - 1) << endl;}return 0;
}
2.10
P1596 [USACO10OCT] Lake Counting S
题目大意:有一个 W 和 . 组成的矩形,W 表示 水,求有几个水塘(即连在一起的 W)
思路:用 dfs 搜索 W 以及周围四个方向的 W,独立搜索(即不是从其它W递归搜索)次数就是水塘水量
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;char c[100][100];
bool vis[100][100];
int n, m;int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};void f(int i, int j) {vis[i][j] = 1;for (int k = 0; k < 8; k++) {if (i + dx[k] >= 0 && i + dx[k] < n && j + dy[k] >= 0 && j + dy[k] <= m && !vis[i + dx[k]][j + dy[k]] && c[i + dx[k]][j + dy[k]] == 'W') {f(i + dx[k], j + dy[k]);}}
}int main() {cin >> n >> m;for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> c[i][j];int s = 0;for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)if (!vis[i][j] && c[i][j] == 'W') {s++;f(i, j);}cout << s;return 0;
}
2.11
P1162 填涂颜色
题目大意:数字 0 组成的方阵中,有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。
思路:要找圈内的0,只需要找到圈外的0,即和边缘相连接的0(dfs和bfs都可以),剩下的0就是圈内的。
#include <vector>
#include "set"
#include "iostream"
#include "unordered_map"
#include "algorithm"
#include "cstring"
#include "math.h"
#include "iomanip"using namespace std;char c[30][30];int setC(int i, int j, int n) {c[i][j] = '0';if (i + 1 < n && c[i + 1][j] == '2') setC(i + 1, j, n);if (i - 1 >= 0 && c[i - 1][j] == '2') setC(i - 1, j, n);if (j + 1 < n && c[i][j + 1] == '2') setC(i, j + 1, n);if (j - 1 >= 0 && c[i][j - 1] == '2') setC(i, j - 1, n);
}int main() {int n;cin >> n;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cin >> c[i][j];}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (c[i][j] == '0')c[i][j] = '2';}}for (int i = 0; i < n; i++) {if (c[i][0] == '2')setC(i, 0, n);if (c[i][n - 1] == '2')setC(i, n - 1, n);}for (int j = 0; j < n; j++) {if (c[0][j] == '2')setC(0, j, n);if (c[n - 1][j] == '2')setC(n - 1, j, n);}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cout << c[i][j] << " ";}cout << endl;}return 0;
}
2.12
P1032 [NOIP 2002 提高组] 字串变换
题目大意:有一些字符串的变换规则,形如 S1 -> S2
求从字符串 A 变换成字符串 B 的最少次数
思路:将变换规则理解为图的一条边,然后以 A 为起点进行广度优先搜索,直到搜索到 B 或则超过次数
#include <sstream>
#include "iostream"
#include "math.h"
#include "algorithm"
#include "string.h"
#include "unordered_set"
#include "deque"
#include "stack"
#include "queue"
#include "vector"
#include "unordered_map"using namespace std;string A, B;
queue<string> q;
unordered_set<string> qq;
vector<vector<string>> rule;int f() {for (int i = 1; i <= 10; i++) {queue<string> q2;while (!q.empty()) {string a = q.front();q.pop();for (const auto &xx:rule) {int k = 0;if (a.size() > xx[0].size()) {k = a.size() - xx[0].size();}for (int j = 0; j <= k; j++) {if (a.substr(j, xx[0].size()) == xx[0]) {string bb = a.substr(0, j) + xx[1] + a.substr(j + xx[0].size());if(qq.find(bb) == qq.end()) {//cout << bb << endl;if (bb == B) {cout << i;exit(0);}q2.push(bb);qq.insert(bb);}}}}}q = q2;}cout << "NO ANSWER!";
}int main() {cin >> A >> B;string r1, r2;while (cin >> r1) {cin >> r2;rule.push_back({r1, r2});}qq.insert(A);q.push(A);if (A == B) cout << 0;f();return 0;
}
2.13
P1825 [USACO11OPEN] Corn Maze S
题目大意:有一块矩形区域,每个格子里可能是障碍物、传送门、可通行区域、初始位置、出口,分别用不同的字符标记,求到出口的最小次数
思路:从初始位置开始,广度优先搜索,记录到达每个格子的最少次数,直到搜到出口
#include <sstream>
#include "iostream"
#include "algorithm"
#include "vector"
#include "unordered_map"
#include "unordered_set"
#include "queue"using namespace std;const int N = 301;
char a[N][N];
int step[N][N], dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
vector<int> b[26];int main() {int n, m, sx, sy;cin >> n >> m;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {cin >> a[i][j];if (a[i][j] == '@') {sx = i, sy = j;} else if (a[i][j] >= 'A' && a[i][j] <= 'Z') {int k = a[i][j] - 'A';b[k].push_back(i);b[k].push_back(j);}}}queue<int> qx, qy;qx.push(sx), qy.push(sy);step[sx][sy] = 1;while (!qx.empty()) {int x = qx.front();int y = qy.front();qx.pop(), qy.pop();for (int i = 0; i < 4; i++) {int xx = x + dx[i];int yy = y + dy[i];if (xx >= 0 && xx < n && yy >= 0 && yy < m) {if (a[xx][yy] == '.' && !step[xx][yy]) {qx.push(xx);qy.push(yy);step[xx][yy] = step[x][y] + 1;//cout << xx << " " << yy << " " << step[xx][yy] << endl;} else if (a[xx][yy] == '=') {cout << step[x][y];return 0;} else if (a[xx][yy] >= 'A' && a[xx][yy] <= 'Z') {int k = a[xx][yy] - 'A';if (b[k][0] == xx && b[k][1] == yy) {xx = b[k][2];yy = b[k][3];} else {xx = b[k][0];yy = b[k][1];}if(!step[xx][yy]) {step[xx][yy] = step[x][y] + 1; // 标记的传送门的终点//cout << xx << " " << yy << " " << step[xx][yy] << endl;qx.push(xx), qy.push(yy);}}}}}return 0;
}