题意:
中国和印度之间有一片地方,把这片地方抽象化,于是就可以看成一个N * M矩阵,其中黑色的代表高山不能
走过去,白色的代表平原,可以通行,人每次可以选择往上下左右四个方向移动,但是随着时间的变化某些白色的平原
会变成黑色的高山,从而变为不可通行,题目中给出一个代表地势的图,然后有 Q 次操作,第 i 次操作 代表在第 i 年
(x, y)处的平原变成了高山,即白色变为了黑色。问中国印度最早彻底断绝的时间,如果在 Q 年后还没有断绝就输出 -1;
思路:
刚开始做的时候一看到图求图的连通性,很自然的用回溯了,这道题用 二分 + 回溯, 二分 + BFS都能做出来,
但是后面觉得还是并查集好理解点,就用并查集又写了一遍,用并查集的思想是如果上下彻底断绝联系,那么说明从
左到右存在一片区域,或者一条线,都是由黑色的方格组成,我们可以在为最左边开辟一个新的节点,最右边也开辟
一个新的节点,如果这俩个节点某一时刻在一个集合里面了那么说明就已经彻底断绝了。
我的方法是:题目中给的图下标是(0 -- N - 1, 0 - -M - 1),我在这里用 (1 -- N,1 -- M )来保存这个图,把第 0 列
和第M + 1都初始化为1,代表最右边最左边刚开始就是不能走的,为最左边开辟一个节点为 0 ,为最右边开辟一个节点
为N * M +1,图中下标为(i, j)的节点以编号 (i - 1) * M + j 代表,也就是图中的点编号从左到右,从上到下分别为1,
2,3,4,5,6.......N * M,如果(i, j)周围八个方向有一个节点的值为 1 那么就可以把这俩个节点并到一个集合里面,在并
的同时查询 0 号节点和 N * M +1号节点是否在一个集合里面,那就说明左右被黑色的联通,上下被彻底断绝联系。
代码:
<pre name="code" class="cpp">#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
const int MAXN = 500;
int pre[MAXN * MAXN + 7];
int Gra[MAXN + 7][MAXN + 7];
//上下左右以及斜着的八个方向
int dirX[] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dirY[] = {-1, 0, 1, -1, 1, -1, 0, 1};
int n, m;
//初始化并查集数组
void initPre()
{
for(int i = 0; i <= n * m + 1; i++)
pre[i] = i;
}
//初始化存放图的数组
void initGra()
{
memset(Gra, -1, sizeof(Gra));
for(int i = 1; i <= n; i++)//×î×ó±ßºÍ×îÓÒ±ßÒ»¿ªÊ¼¾ÍÊDz»Á¬Í¨µÄ
Gra[i][0] = Gra[i][m + 1] = 1;
}
//带压缩路径的查找操作
int Find(int x)
{
return x == pre[x] ? x : pre[x] = Find(pre[x]);
}
void mix(int x, int y)
{
int fx = Find(x);
int fy = Find(y);
if(fx > fy) pre[fx] = fy;
if(fx < fy) pre[fy] = fx;
}
//把(i,j)点和其八个方向的不能走的地方并到一个集合里面
void deal(int i, int j)
{
for(int s = 0; s < 8; s++)
{
int x = i + dirX[s];
int y = j + dirY[s];
if(Gra[x][y] == 1)
{
if(y == 0) mix(0, (i - 1) * m + j);//最左边
else if(y == m + 1) mix(n * m + 1, (i - 1) * m + j);//最右边
else mix((x - 1) * m + y, (i - 1) * m + j);
}
}
}
int main()
{
//freopen("in.txt", "r", stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n, &m);
initPre();
initGra();
for(int i = 1; i <= n; i++ ){
for(int j = 1; j <= m; j++)
{
scanf("%1d",&Gra[i][j]);
if(Gra[i][j] == 1)
deal(i , j);
}
}
int Q;
scanf("%d",&Q);
int flag = 0;//是否彻底断绝
int ans = -1;
if(Find(0) == Find(n * m + 1)){ans = 0; flag = 1; }
for(int i = 1; i <= Q; i++)
{
int x, y;
scanf("%d%d",&x, &y);
Gra[x + 1][y + 1] = 1;
deal(x + 1, y + 1);
if( !flag && Find(0) == Find(n * m + 1)){//如果代表最左边的节点和代表最右边的节点在一个集合里面,说明左右联通了,上下断绝了
ans = i;
flag = 1;
}
}
printf("%d\n",ans);
}
return 0;
}