之前RXZ说做了一道题没感觉就没必要写笔记,所以一直没写,但是今天遇到了一道题。非常有意思,是一种我不知道的做法。
洛谷 P1141 01迷宫
由于放链接会发生一些不妙的事情,请自行去洛谷查看。
题意没有任何的弯弯绕绕。
看到题的tag是广搜就先写了个bfs,然后Tle了3个点。
bfs tle代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <cstdio> #include <queue> #include <utility> #include <cstring>
using namespace std;
const int MAXN = 1e3 + 5; const int dx[] = {-1, +1, 0, 0}; const int dy[] = {0, 0, +1, -1};
int n, m, ans; bool visit[MAXN][MAXN], map[MAXN][MAXN];
void input() { scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf("%1d", &map[i][j]); }
int get(int i, int j) { memset(visit, 0, sizeof(visit)); queue<pair<int, int>> q; int ans = 0;
q.push({i, j});
while (!q.empty()) { int x = q.front().first, y = q.front().second; q.pop();
if (!visit[x][y]) { visit[x][y] = true; ans++; }else continue;
for (int i = 0; i < 4; i++) { int nx = x + dx[i], ny = y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n) if (map[nx][ny] ^ map[x][y]) q.push({nx, ny}); } }
return ans; }
void work() { while (m--) { int x, y; scanf("%d %d", &x, &y);
printf("%d\n", get(x, y)); } }
int main() { input();
work();
return 0; }
|
非常的不妙,愉快的重写了个dfs自以为能省点时间(然而事实上只减少了0.01s的时间,可以看作误差)。
dfs tle代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <cstdio> #include <queue> #include <utility> #include <cstring>
using namespace std;
const int MAXN = 1e3 + 5; const int dx[] = {-1, +1, 0, 0}; const int dy[] = {0, 0, +1, -1};
int n, m, ans; bool visit[MAXN][MAXN], map[MAXN][MAXN];
void input() { scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf("%1d", &map[i][j]); }
void dfs(int x, int y) { if (!visit[x][y]) { visit[x][y] = true; ans++; } else return;
for (int i = 0; i < 4; i++) { int nx = x + dx[i], ny = y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n) if (map[nx][ny] ^ map[x][y]) dfs(nx, ny); } }
void work() { while (m--) { int x, y; scanf("%d %d", &x, &y);
memset(visit, 0, sizeof(visit)); ans = 0; dfs(x, y); printf("%d\n", ans); } }
int main() { input();
work();
return 0; }
|
再想了10分钟还是没头绪,就无奈的打开了题解(说好的学术模式)。
自豪的说一句,我的代码的可读性比他们的高不少:
dfsAC代码!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include <cstdio> #include <queue> #include <cstring>
using namespace std;
const int MAXN = 1e3 + 5; const int dx[] = {-1, +1, 0, 0}; const int dy[] = {0, 0, +1, -1};
int n, m, used[MAXN][MAXN], ans[MAXN * MAXN + 5]; bool map[MAXN][MAXN];
void input() { scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf("%1d", &map[i][j]); }
void dfs(int x, int y, int count) { if (used[x][y] != -1) return;
used[x][y] = count; ans[count]++;
for (int i = 0; i < 4; i++) { int nx = x + dx[i], ny = y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n) if (map[nx][ny] ^ map[x][y]) dfs(nx, ny, count); } }
void work() { memset(used, -1, sizeof(used));
for (int i = 1; i <= m; i++) { int x, y; scanf("%d %d", &x, &y);
if (used[x][y] == -1) dfs(x, y, i); else ans[i] = ans[used[x][y]]; }
for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); }
int main() { input();
work();
return 0; }
|
他们判断是否能走的方法真是让我哭笑不得:
1 2 3 4 5
| void dfs(int r,int c,int z,int lll){ if (r<0 || r>=n || c<0 || c>=n || f[r][c]!=-1 || s[r][c]-'0'!=z)return; f[r][c]=lll;ans[lll]++; dfs(r-1,c,!z,lll);dfs(r+1,c,!z,lll);dfs(r,c-1,!z,lll);dfs(r,c+1,!z,lll); }
|
看了我几分钟才看明白是通过z变量来判断是否通,难道他们不知道有个东西叫异或运算符吗?
‘^’ :只有当两个不一样的时候才会返回true,否则返回false,多么方便啊!
总结
是连通块做法,我之前虽然听说过但是不会用,在做完这道题后就初入连通块了!
大致思路就是用ans数组记录连通块的value,used数组(其实叫flag数组更合适)记录每个坐标所属的ans。
在搜索的时候就给连通块标号和计算通路,这样即可AC。
This post was written on August 14, 2022 at 11:16 PM.