并查集 T2 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

题目描述

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

思路

能够封圈的最后一条边所连接的两个点a b, 这两个点一定已经存在一条路径使其连通, 也就是在一个集合内。

故只需要判断边的两端点是否在一个集合内即可。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
#define x first
#define y second
const int N = 220;
typedef pair<int, int> PII;
PII point[N][N];
PII f[N][N];
 
PII find(PII x)
{
    if (x != f[x.x][x.y])
        f[x.x][x.y] = find(f[x.x][x.y]);
    return f[x.x][x.y];
}
 
void merge(PII a, PII b)
{
    f[a.x][a.y] = f[b.x][b.y];
}
 
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            f[i][j] = {i, j};
 
    bool flag = false;
    int res = 0;
    for (int i = 1; i <= m; i++)
    {
        int a, b, x, y;
        char s[2];
        cin >> a >> b >> s;
        if (s[0] == 'D')
            x = a + 1, y = b;
        else
            x = a, y = b + 1;
 
        PII pa = find({a, b}), pb = find({x, y});
        if (pa != pb)
            merge(pa, pb);
        else if (!flag)
        {
            flag = true;
            res = i;
        }
    }
    if (flag)
        cout << res << endl;
    else
        cout << "draw\n";
 
    return 0;
}