1347:【例4-8】格子游戏——二维并查集

本文介绍了一款基于二维并查集的经典游戏算法实现过程,通过动态维护连通性来判断游戏是否结束。利用结构体存储顶点坐标,实现快速查找和合并操作,确保高效判断两点间是否构成封闭圈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目描述】
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

分析

  1. 并查集解决的是连通性(无向图连通分量)和传递性(家谱关系)问题,并且可以动态的维护。抛开格子不看,任意一个图中,增加一条边形成环当且仅当这条边连接的两点已经联通,于是可以将点分为若干个集合,每个集合对应图中的一个连通块。( 参考:1347 格子游戏 (并查集)
  2. 此题的解题核心就是:两个已连通的点之间,再添加一条边就构成了一个环;,所以此题采用二维并查集来做,如果两个点已连通(祖先相同),那就满足题意,否则的话将这两个点之间建立联系(将这两个点连通起来);
  3. 用了一个结构体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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向上的yyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值