DFS中的多次询问问题,并查集拓展,做标记,P1141 01迷宫
dfs是时间复杂度很高的算法,如果题目多次询问而我们每次询问都进行一次完整的dfs大概率是要超时的。所以我们要记忆化搜索,做标记,这里就要有一个好做标记的方法了:这里我介绍一种按询问次数做标记的方法。
题目描述
有一个仅由数字 00 与 11 组成的n×n 格迷宫。若你位于一格 00 上,那么你可以移动到相邻 44 格中的某一格 11 上,同样若你位于一格 11 上,那么你可以移动到相邻 44 格中的某一格 00 上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
输入格式
第一行为两个正整数 n,m。
下面 n 行,每行 n 个字符,字符只可能是 0 或者 1,字符之间没有空格。
接下来 m 行,每行两个用空格分隔的正整数i,j,对应了迷宫中第i 行第 j 列的一个格子,询问从这一格开始能移动到多少格。
输出格式
m 行,对于每个询问输出相应答案。
输入输出样例
输入 #1复制
2 2 01 10 1 1 2 2
输出 #1复制
4 4
说明/提示
所有格子互相可达。
对于 20%20% 的数据,n≤10;
对于 40%40% 的数据,n≤50;
对于 50%50% 的数据,m≤5;
对于 60%60% 的数据,n≤100,m≤100;
对于 100%100% 的数据,n≤1000,m≤100000。
题目的简而言之就是问:给你一个坐标,能按题目中的要求与多少个其他坐标位置连通,给出与之连通的个数;
这里涉及到连通性,我们考虑用并查集,要怎么用呢?
我们这里的并查集的初始为0,之后祖先节点为m,这里的m次询问为:点y,x第一次被访问到是第m次询问
这样我们当遇到要重复访问的点时,f[y][x]!=0,f[y][x]==m,此时我们只要去找我们之前的第m次询问的答案记录就行了;
这里我们需要一个数组ans记录之前的答案,这里的ans就是按询问次数做标记的方法,但这个方法需要搭配上述几种或其它的方法使用。
将上述思路写成代码就是这样:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cstdio>
#include<iomanip>
using namespace std;
typedef long long LL;
const int N = 1e3 + 5;
string arr[N];
int vis[N][N];
int n, m,ay[]={0,1,0,-1},ax[]={1,0,-1,0},ans[100005],f[N][N], num;
void dfs(int y,int x,int I) {
vis[y][x] = 1;
for (int i = 0,tx,ty; i < 4; i++) {
ty = y + ay[i];
tx = x + ax[i];
if (ty > 0 && ty <= n && tx > 0 && tx <= n && arr[ty][tx] != arr[y][x] && vis[ty][tx] == 0) {
num++;
f[ty][tx] = I;
dfs(ty, tx,I);
}
else if(ty > 0 && ty <= n && tx > 0 && tx <= n && arr[ty][tx] != arr[y][x]) {
f[y][x] = f[ty][tx];
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
cin >>arr[i];
arr[i].insert(0, " ");
}
for (int i = 1,tx,ty; i <= m; i++) {
num = 1;
scanf("%d%d", &ty, &tx);
f[ty][tx] = i;
dfs(ty, tx,i);
num = max(num, ans[f[ty][tx]]);
ans[i] = num;
printf("%d\n", num);
}
return 0;
}
对于这道题,要有另一种做标记的方法,定义一个数组f[][],他即表示对应的点有没有被访问过,又表示该点是在第几次询问时被访问的,
该数组初始化为0,表示没被访问过;
#include <iostream>
#include<stdio.h>
#include<string>
using namespace std;
const int MAX = 1e7;
int d[MAX], f[1010][1010];
string a[1010];
int n, m, cnt = 1;
int xx[4] = { 0,0,1,-1 };
int yy[4] = { 1,-1,0,0 };
void dfs(int x, int y)
{
f[x][y] = cnt, d[cnt]++;
int x1 = x, y1 = y;
for (int i = 0; i < 4; i++)
{
x += xx[i];
y += yy[i];
/*if (x == 2 && y == 1) {
cout << "?" << endl;
}*/
if (x > 0 && x <= n && y > 0 && y <= n && f[x][y] == 0 && a[x][y] != a[x1][y1])
dfs(x, y);
x -= xx[i];
y -= yy[i];
}
}
int main()
{
int xx, yy;
scanf("%d%d ", &n, &m);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i].insert(0, " ");
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (f[i][j] == 0)
{
cnt++;
dfs(i, j);
}
}
for (int i = 1,t1,t2; i <= m; i++)
{
scanf("%d%d", &t1, &t2);
printf("%d\n", d[f[t1][t2]]);
}
return 0;
}