当前位置:首页 > 技术 > 正文内容

基于带权并查集的食物链关系判定

访客 技术 2026年6月2日 1

题目来源:POJ 1182

问题描述:

在一片虚拟的动物世界中,存在三类生物 A、B 和 C,它们构成一个循环捕食系统:A 捕食 B,B 捕食 C,C 捕食 A。现有 N 只编号为 1 到 N 的动物,每只属于其中一类,但具体类别未知。接下来会给出 K 条语句,每条语句描述两个个体之间的关系,形式如下:

  • 类型 1:X 与 Y 是同类。
  • 类型 2:X 捕食 Y。

这些语句可能为真也可能为假。若某句话满足以下任一条件,则视为假话:

  1. 与之前已确认为真的陈述矛盾;
  2. X 或 Y 超出合法范围 [1, N];
  3. 声称某个个体捕食自己(即 X 吃 X)。

目标是统计所有被判定为假话的语句总数。

输入格式:

首行包含两个整数 N 和 K,分别表示动物数量和语句条数。随后 K 行,每行三个整数 D、X、Y,D 表示语句类型(1 或 2),X 和 Y 为涉及的动物编号。

输出格式:

输出一个整数,代表假话的总数量。

样例输入:

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

样例输出:

3

解法思路:

采用扩展并查集结构来维护动物间的相对关系。核心思想是将每个节点 x 拆分为三个角色:

  • x 表示其本身;
  • x + N 表示它的天敌(捕食者);
  • x + 2*N 表示它的猎物(被它捕食的对象)。

通过这种方式,可以利用并查集维护这三种状态之间的等价类。例如:

  • 当 X 与 Y 是同类时,应合并 (x, y)、(x+N, y+N)、(x+2N, y+2N),以保持角色一致性;
  • 当 X 捕食 Y 时,说明 X 是 Y 的捕食者的同类,因此需要合并 (x, y+N),同时推导出其他对应关系:x 的猎物(x+2N)应等于 y(y 本身为其猎物的猎物),x+N 应连接到 y+2N。

在处理每条语句前,先判断是否违反基本规则(越界或自食)。然后根据当前语句类型检查是否存在冲突——即是否已有路径表明其不可能成立。如果没有冲突,则执行相应的合并操作;否则计数器加一。

实现代码如下:

#include <cstdio>

const int MAX_SIZE = 50005;
int parent[3 * MAX_SIZE];
int rank[3 * MAX_SIZE];

// 初始化并查集
void initialize(int size) {
    for (int i = 0; i < size; ++i) {
        parent[i] = i;
        rank[i] = 0;
    }
}

// 查找根节点(带路径压缩)
int findRoot(int node) {
    if (parent[node] != node)
        parent[node] = findRoot(parent[node]);
    return parent[node];
}

// 合并两个集合(按秩合并)
void mergeSets(int u, int v) {
    int rootU = findRoot(u);
    int rootV = findRoot(v);
    if (rootU == rootV) return;

    if (rank[rootU] < rank[rootV]) {
        parent[rootU] = rootV;
    } else {
        parent[rootV] = rootU;
        if (rank[rootU] == rank[rootV])
            rank[rootU]++;
    }
}

// 判断两节点是否在同一集合
bool isConnected(int a, int b) {
    return findRoot(a) == findRoot(b);
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);

    initialize(3 * n);
    int falseCount = 0;

    for (int i = 0; i < k; ++i) {
        int type, x, y;
        scanf("%d %d %d", &type, &x, &y);

        // 编号转为0基索引
        --x; --y;

        // 检查是否越界
        if (x < 0 || x >= n || y < 0 || y >= n) {
            falseCount++;
            continue;
        }

        if (type == 1) {  // 同类关系
            if (isConnected(x, y + n) || isConnected(x, y + 2 * n)) {
                falseCount++;
            } else {
                mergeSets(x, y);
                mergeSets(x + n, y + n);
                mergeSets(x + 2 * n, y + 2 * n);
            }
        } else {  // 捕食关系
            if (x == y || isConnected(x, y) || isConnected(x, y + 2 * n)) {
                falseCount++;
            } else {
                mergeSets(x, y + n);
                mergeSets(x + n, y + 2 * n);
                mergeSets(x + 2 * n, y);
            }
        }
    }

    printf("%d\n", falseCount);
    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。