UVA 10158 War (并查集)

本文介绍了一个基于不相交集合(并查集)的数据结构解决战争中A国与B国和谈过程中的人际关系推理问题的方法。通过setFriends和setEnemies操作设定人际关系,利用areFriends和areEnemies判断两人关系。

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

翻译转自该博客:http://blog.youkuaiyun.com/metaphysis/article/details/6848901

问题描述
一场战争在 A 国和 B 国之间开始了。作为一位 C 国的好公民,你决定为你的国家秘密的参加 A 国与 B  
 国之间的和谈。和谈中还有 n 个人,但你不知道他们分别属于哪个国家。你可以看到他们互相交谈,并能通  
 过观察双方在一对一交谈时的表现猜测他们是敌人还是朋友。  

 你的国家需要知道某些特定的两个人之间的关系:到底是属于同一国,还是互相敌对,因此需要你在和谈期间  
 接收政府传来的问题,并根据你当前的观察做出回答。  

 正式的来说,考虑一个可以进行如下操作的黑盒:  

      setFriends(x,y)   x 和 y 属于同一国家  
      setEnemies(x,y)   x 和 y 属于不同国家  
      areFriends(x,y)   仅当你确信 x 和 y 为朋友时返回 true  
      areEnemies(x,y)   仅当你确信 x 和 y 为敌人时返回 false  

 若前两种操作与你现有的结论相冲突,你应该报错。“朋友” 关系(符号为 ~)和 “敌对” 关系(符号为 *)  
 具有如下性质:  

      ~ 是等价关系,即:  
      1. 若 x ~ y 且 y ~ z,则 x ~ z(朋友的朋友也是朋友)  
      2. 若 x ~ y,则 y ~ x(朋友是相互的)  
      3. x ~ x(每个人都是他自己的朋友)  

      * 满足对称性和反自反性:  
      1. 若 x * y,则 y * x(敌对是相互的)  
      2. x * x 总为假(没有人和自己敌对)  
      3. 若 x * y,且 y * z,则 x ~ z(敌人的敌人是朋友)  
      4. 若 x ~ y,且 y * z,则 x * z(敌人的朋友是敌人)  

 操作 setFriends(x,y) 和 setEnemies(x,y) 不能破坏上述性质。
输入
  输入每一行包含一个整数 n,表示人数。接下来第一行有三个数,c x y,其中 c 为操作编号。  

      c = 1setFriends  
      c = 2setEnemies  
      c = 3,areFriends  
      c = 4,areEnemies  

 x 和 y 分别为操作的参数,它们都是区间 [0,n) 内的整数,表示两个不同的人。最后一行为 0 0 0。  

 输入中所有整数由至少一个空格或换行符隔开。最多可有 10 000 个人,但操作总数没有限制。  
输出
对于每个 areFriends 和 areEnemies 操作,输出 “0” (表示假)或者 “1” (表示真)。对于每个  
 与当前结论冲突的 setFriends 或 setEnemies 操作,你的程序应当输出 “-1”,但该指令不应对后续  
 操作的执行产生任何影响。成功的 setFriends 或 setEnemies 不产生输出。  

 输出中的相邻整数间应换行。 
解析:

此题可以应用不相交集合数据结构(并查集)来解决。
数组 root 的元素从 [0,N - 1] 存放的是国家 [0,N - 1] 的朋友,元素 [N,2 * N - 1] 存放的是国家 [0,N - 1] 的敌人。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 10005;
int pa[N*2];
int n;
void init() {
    for(int i = 0; i < 2 * n; i++) {
        pa[i] = i;
    }
}
int find(int x) {
    if(x == pa[x])
        return x;
    else
        return pa[x] = find(pa[x]);
}
void solve() {
    int c, x, y;
    while(scanf("%d%d%d",&c ,&x ,&y) != EOF) {
        if(!c && !x && !y) break;
        int xf = find(x) , xe = find(x+n);
        int yf = find(y) , ye = find(y+n);
        if(c == 1) {
            if(xf == ye)
                printf("-1\n");
            else {
                pa[xf] = yf;
                pa[xe] = ye;
            }
        }else if(c == 2) {
            if(xf == yf)
                printf("-1\n");
            else {
                pa[xf] = ye;
                pa[xe] = yf;
            }
        }else if(c == 3) {
            printf("%d\n", xf == yf);
        }else if(c == 4) {
            printf("%d\n", xf == ye);
        }
    }
}
int main() {
    while(scanf("%d", &n) != EOF) {
        init();
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值