uva 10158 War

原题:
A war is being lead between two countries, A and B. As a loyal citizen of C, you decide to help your countrys espionage by attending the peace-talks taking place these days (incognito, of course). There are n people at the talks (not including you), but you do not know which person belongs to which country. You can see people talking to each other, and through observing their behaviour during their occasional one-to-one conversations, you can guess if they are friends or enemies. In fact what your country would need to know is whether certain pairs of people are from the same country, or they are enemies. You may receive such questions from Cs government even during the peace-talks, and you have to give replies on the basis of your observations so far. Fortunately nobody talks to you, as nobody pays attention to your humble appearance.
Now, more formally, consider a black box with the following operations:
setFriends(x,y)
setEnemies(x,y)
areFriends(x,y)
areEnemies(x,y)
shows that x and y are from the same country
shows that x and y are from different countries
returns true if you are sure that x and y are friends
returns true if you are sure that x and y are enemies
The first two operations should signal an error if they contradict with your former knowledge. The two relations ‘friends’ (denoted by ∼) and ‘enemies’ (denoted by ∗) have the following properties:
∼ is an equivalence relation, i.e.
1. If x ∼ y and y ∼ z then x ∼ z (The friends of my friends are my friends as well.
2. If x ∼ y then y ∼ x (Friendship is mutual.)
3. x ∼ x (Everyone is a friend of himself.)
∗ is symmetric and irreflexive
1. If x ∗ y then y ∗ x (Hatred is mutual.)
2. Not x ∗ x (Nobody is an enemy of himself.)
Also
1. If x ∗ y and y ∗ z then x ∼ z (A common enemy makes two people friends.
2. If x ∼ y and y ∗ z then x ∗ z (An enemy of a friend is an enemy.
Operations setFriends(x,y) and setEnemies(x,y) must preserve these properties.
Input
The first line contains a single integer, n, the number of people.
Each of the following lines contains a triple of integers, cxy, where c is the code of the operation:
c = 1,
c = 2,
c = 3,
c = 4,
setFriends
setEnemies
areFriends
areEnemies
and x and y are its parameters, which are integers in the range [0, n), identifying two (different) people.
The last line contains ‘0 0 0’.
All integers in the input file are separated by at least one space or line break. The only constraint
is n < 10000, the number of operations is unconstrained.
Output
For every “areFriends” and “areEnemies” operation write ‘0’ (meaning no) or ‘1’ (meaning yes) to the output. Also for every “setFriends” or “setEnemies” operation which contradicts with previous knowledge, output a ‘-1’ to the output; note that such an operation should produce no other effect and execution should continue. A successful “setFriends” or “setEnemies” gives no output. All integers in the output file must be separated by one line break.
Sample Input
10
1 0 1
1 1 2
2 0 5
3 0 2
3 8 9
4 1 5
4 1 2
4 8 9
1 8 9
1 5 2
3 5 2
0 0 0

Sample Output
1
0
1
0
0
-1
0

中文:
有n个人分别输入两个不同的国家,他们再谈判,现在让你通过他们之间的谈话内容判断对方是敌是友。
有4种操作,分别是setFriends(x,y),setEnemies(x,y),areFriends(x,y),areEnemies(x,y),用1到4个数字来标记

如果是set..操作,如果与之前的判断冲突,则输出-1,否则不输出。如果是are操作..如果判断正确输出1否则输出0

#include <bits/stdc++.h>
using namespace std;
const int maxn=10001;
int father[maxn],offset[maxn];
int Find(int x)
{
    if(x!=father[x])
    {
        int tmp=father[x];
        father[x]=Find(father[x]);
        offset[x]=(offset[x]+offset[tmp])%2;
//        cout<<"find "<<x<<" "<<tmp<<" "<<offset[x]<<endl;
    }
    return father[x];
}
int Union(int x,int y,int d)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx==fy)
    {
//        cout<<"union "<<offset[x]<<" "<<offset[y]<<endl;
        if((offset[x]-offset[y]+2)%2!=d)
            return -1;
        else
            return 1;
    }
    father[fy]=fx;
    offset[fy]=(offset[x]-offset[y]+d+2)%2;
 //   cout<<"union "<<offset[x]<<" "<<offset[y]<<endl;
    return 0;
}
void init(int n)
{
    for(int i=0;i<=n;i++)
        father[i]=i;
    memset(offset,0,sizeof(offset));
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        int x,y,d;
        init(n);
        while(cin>>d>>x>>y,x+y+d)
        {
            if(d<=2)
            {
                d--;
                if(Union(x,y,d)==-1)
                {
                    cout<<-1<<endl;
                }
            }
            else
            {
                d-=3;
                father[y]=Find(y);
//                cout<<"......."<<endl;
                father[x]=Find(x);
//                cout<<"offset "<<offset[x]<<" "<<offset[y]<<endl;
//                cout<<"father "<<father[x]<<" "<<father[y]<<endl;
                if(father[x]==father[y]&&(offset[x]-offset[y]+2)%2==d)
                    cout<<1<<endl;
                else
                    cout<<0<<endl;
            }
        }
        //break;
    }
    return 0;
}

思路:
非常经典的并查集加向量偏移,第一次做这类题的时候是poj上的食物链,我记得我刚学并查集的时候遇到这题,由于当时对递归理解的不好,这题简直把我恶心坏了。没想到能在uva上再次遇到它,真是怀念。
向量偏移就是在并查集的路径压缩过程也就是子节点与父节点之间变换时,维持关系变换使得给出的节点关系能够通过其父节点之间的关系得到分类的操作。
现在设置偏移量offset[]用来记录当前节点与父节点的偏移量(因为并查集当中每个节点都只有一个父节点)
以此题为例,只需设置两种关系,朋友与敌人。
现在设置偏移量offset[x]=0时表示x与父节点的关系是朋友,offset[x]=1时表示x与父节点的关系是敌人。

那么,如图所示的原始关系,可以转换成如下关系(两方敌对那么两个节点之间的值是1,否则是0,箭头方向表示父节点方向)

这里写图片描述
转化后
这里写图片描述

敌人的敌人变成朋友,所以1和3是一伙的

那么在Find()过程,也就是查找根节点的过程当中,通过路径压缩判断当前节点与父节点至今的关系。
公式为 offset[x]=(offset[x]+offset[father[x]])%2 x节点与新的父节点之间的偏移量等于,之前x与父节点的偏移量加上父节点与爷爷节点之间偏移量的和,模2的目的是因只有两种状态,也就是朋友和敌人,如果不用模运算,就得用if判断,更麻烦。
这里写图片描述
在合并的过程当中,也就是设置两个节点之间关系的过程。
首先找到两个节点的x,y的父节点fx,fy。如果fx和fy相等,说明x和y之前连接过,已经有关系了,那么就判断当前要设置的关系是否和之前的关系冲突。判断冲突的方法是判断x与y对它们相同根节点的偏移量是否相同,如果x和y都认为根节点是敌人或是朋友,那么x和y肯定是同伙,否则它们就是敌人,使用公式
(offset[x]-offset[y]+2)%2!=d d是x与y要设置的关系,只有0和1两种,0是朋友,1是敌人。 公式结果为真,说明与之前的冲突。

如果要设置的两个节点x和y的父节点不相同,说明x和y之间还没有建立关系,也就是不知道是朋友还是敌人。
那么,通过给定的关系d,把x与y建立关系。

首先,把y的父节点fy连接到fx上面,表示x和y之前,以及与x,y向量的所有数据连接在一起。
然后,计算新连接的节点对父节点的偏移值(之前fx作为fy的父节点,那么就要计算fy与fx的偏移值)

怎么计算偏移值? 已经有了x与fx之间的偏移值,y与fy之间的偏移值,x与y之间的偏移值(就是要设置x与y之间的关系值d),现在要求fx与fy之间的值。,利用向量的知识。
这里写图片描述
改变箭头指向,只需要用种类数减去offset值即可,这里的种类数为2(敌人和朋友)
(如果fy与y相等,那么offset[y]=0)
这里写图片描述
最后通过上面的计算就可以得到种类结果了,种类数同样可以推广到3个

挑战程序设计竞赛还有另外一种方法,书上有详述

给出一组数据,自己手动模拟一遍

5
2 0 1
2 1 2
2 2 3
3 0 2
3 1 2
0 0 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值