【题目描述】
Alice和Bob玩了一个古老的游戏:首先画一个n × n的点阵(下图n = 3)
接着,他们两个轮流在相邻的点之间画上红边和蓝边:
直到围成一个封闭的圈(面积不必为1)为止,“封圈”的那个人就是赢家。因为棋盘实在是太大了(n ≤ 200),他们的游戏实在是太长了!他们甚至在游戏中都不知道谁赢得了游戏。于是请你写一个程序,帮助他们计算他们是否结束了游戏?
【输入】
输入数据第一行为两个整数n和m。m表示一共画了m条线。以后m行,每行首先有两个数字(x, y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是"D ",则是向下连一条边,如果是"R "就是向右连一条边。输入数据不会有重复的边且保证正确。
【输出】
输出一行:在第几步的时候结束。假如m步之后也没有结束,则输出一行“draw”。
【输入样例】
3 5
1 1 D
1 1 R
1 2 D
2 1 R
2 2 D
【输出样例】
4
分析
- 并查集解决的是连通性(无向图连通分量)和传递性(家谱关系)问题,并且可以动态的维护。抛开格子不看,任意一个图中,增加一条边形成环当且仅当这条边连接的两点已经联通,于是可以将点分为若干个集合,每个集合对应图中的一个连通块。( 参考:1347 格子游戏 (并查集))
- 此题的解题核心就是:两个已连通的点之间,再添加一条边就构成了一个环;,所以此题采用二维并查集来做,如果两个点已连通(祖先相同),那就满足题意,否则的话将这两个点之间建立联系(将这两个点连通起来);
- 用了一个结构体node去存一个顶点的横坐标、纵坐标,f数组来存一个顶点的结构体信息(f[x][y]:存储(x,y)这个结点的横坐标、纵坐标);在每次输入的一行操作,先判断当前的两点是否已连通,两个已连通的点之间,再添加一条边就构成了一个环;不连通,那就连起来
分析
#include <bits/stdc++.h>
using namespace std;
struct node {
int x, y;
};
const int N = 205;
node f[N][N];//f[x][y]:存储(x,y)这个结点的横坐标、纵坐标
int n, m;
node find(node k) {
if (f[k.x][k.y].x == k.x && f[k.x][k.y].y == k.y)
return f[k.x][k.y];
return f[k.x][k.y] = find(f[k.x][k.y]);
}
void merge(node k1, node k2) {
k1 = find(k1);
k2 = find(k2);
f[k1.x][k1.y] = k2;
}
int main() {
cin.tie(0);
cin >> n >> m;
int x, y;
char c;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
f[i][j].x = i;
f[i][j].y = j;
}
}
node k1, k2;
for (int i = 1; i <= m; ++i) {
cin >> x >> y >> c;
if (c == 'D') {
//连接(x,y)和(x+1,y)这两个结点
k1 = find(f[x][y]);
k2 = find(f[x + 1][y]);
} else {
k1 = find(f[x][y]);
k2 = find(f[x][y + 1]);
}
//先判断当前的两点是否已连通,两个已连通的点之间,再添加一条边就构成了一个环
if (k1.x == k2.x && k1.y == k2.y) {
cout << i;
return 0;
} else {
//不连通,那就连起来
merge(k1, k2);
}
}
cout << "draw";
return 0;
}