題目連接:http://algorithm.openjudge.cn/betaexam/B/
(題目源自Jon Kleinberg著《Algorithm Design》第3章圖論課後習題第4題,英文原版第107頁)
描述
有一羣旅行愛好者,有一天,他們帶回了n只蝴蝶回來。他們相信每一隻都屬於兩個不同種類中的一種,爲了討論方便,我們稱它們爲A與B。他們想把n只標本分成兩組——一些屬於A且一些屬於B——但是直接標記任何一個標本對於他們是非常困難,因此他們決定採用下面的方法。
對每對標本i和j,他們細心地把它們放到一起研究。如果他們以自己的判斷足以確信,那麼他們把這對蝴蝶標記爲“相同”(這意味着他們相信這兩隻來自同一類)或者是“不同”(這意味着他們相信這兩隻來自不同的類)。他們也可以對某些標本判斷不出來而棄權,在這種情況下,我們稱這對標本是不明確的。
現在他們有n只標本的集合,還有對那些沒有棄權的標本對的m個判斷的集合(“相同”或者“不同”)。他們想知道這個數據與每隻蝴蝶來自A和B中的一個類的想法是否一致。更具體地說,如果對每對蝴蝶按照下述方式標記A或B是可能的,即對每個標爲“相同”的(i,j)對,就是i與j有相同標記的情況;對每個標爲“不同”的(i,j)對,就是i與j有不同標記的情況。那麼我們可以說這m個判斷是一致的。他們正在冥思苦想自己的判斷是否是一致的。請你幫他們設計合理的算法解決該問題。
輸入
輸入包含多組數據,以文件結束符爲終止。
每組數據第一行爲兩個整數,分別是n和m:
n爲蝴蝶的數量,編號從0到n-1
m爲關係的數量
接下來是m組關係數據,每組數據佔一行,爲三個整數,前兩個整數表示蝴蝶的編號,第三個整數爲關係的種類(相同或者不同):
0爲相同,1爲不同
1 < n <= 1000
1 < m <= 100000
輸出
合理就輸出YES
不合理就輸出NO
樣例輸入
3 3
0 1 0
1 2 1
0 2 1
3 3
0 1 0
1 2 1
0 2 0
樣例輸出
YES
NO
提示
注意輸出的大寫和回車
題意
n個不是A類就是B類的蝴蝶,告訴你一些蝴蝶對是相同的還是不同的,讓你判斷這樣的情況是合理(YES)還是不合理的(NO)。
解題思路
核心思想就是判斷一個圖是不是一個二部圖,但是存在一些特殊的情況需要注意。把蝴蝶對看成一條邊的話,有些點是沒有邊相連的,那麼這些點屬於哪一類對結果都沒有影響。更甚,這個圖可能是由多個連通子圖組成的,各個子圖間是相互獨立的。我的解題思路就是從某一點出發,假設這點是A類,然後把相鄰邊的點按照邊的權值來染色,如果染色矛盾就跳出輸出NO,如果BFS遍歷完所有點沒有矛盾就是YES。
BFS代碼
#include<bits/stdc++.h>
using namespace std;
int n,m,link[1005][1005],state[1005];
bool suc,vis[1005];
queue<int> vist;
bool bfs(int node)
{
if(vis[node]) return true;
vis[node]=1;
if(!state[node]) state[node]=1;
for(int i=0;i<n;i++){
if(link[node][i]==1){
if(!state[i]) state[i]=3-state[node];
else if(state[i]==state[node]){
suc=false;
//cout<<node<<' '<<i<<"###"<<endl;
return false;
}
if(!vis[i]) vist.push(i);
}
else if(link[node][i]==2){
if(!state[i]) state[i]=state[node];
else if(state[i]!=state[node]){
suc=false;
//cout<<node<<' '<<i<<"###"<<endl;
return false;
}
if(!vis[i]) vist.push(i);
}
}
return true;
}
int main()
{
int a,b,c;
while(~scanf("%d%d",&n,&m)){
memset(link,0,sizeof(link));
memset(state,0,sizeof(state));
memset(vis,0,sizeof(vis));
suc=true;
while(!vist.empty()) vist.pop();
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
link[a][b]=2-c;
link[b][a]=2-c;
}
for(int i=0;i<n;i++){
vist.push(i);
while(!vist.empty()){
if(!bfs(vist.front())){
suc=false;
break;
}
vist.pop();
}
if(!suc) break;
}
if(suc) printf("YES\n");
else printf("NO\n");
}
return 0;
}
後記
這道題還可以利用帶偏移的並查集來求解,後期等我AC了再來更新吧。