uva 10158 War

來源:互聯網
上載者:User

原題:
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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.