对于每个言论,可以确定一个关系,如果是x y im ,则说明x和y是同一类人,如果是x y cr ,则说明x和y不是一类人。可以把所有有联系的人放在一个图中,对图中的点进行染色,那么图中的点各自有着直接或间接地联系,那么每个点的颜色非黑即白。为了方便实现,若x , y是同一类人,那么x,y之间有一条权为1的边,若x,y不是同一类人,那么x,y之间有一条权为0的边。根据已有的边,就可以对点进行染色了。若染色过程中存在冲突,那就无解
code:
#include <bits/stdc++.h>
using namespace std ;
const int N = 2e5+10 ;
vector<pair<int,int>>v[N] ;
int t , n , m , flag , cnt0 , cnt1;
bool vis[N] ;
int color[N] ;
void dfs(int u){
vis[u] = true ;
if(color[u] == 0)cnt0++;
else cnt1++;
for(auto it : v[u]) {
if(!vis[it.first]) {
color[it.first] = color[u] ^ it.second ;
dfs(it.first);
}else{
if((color[it.first] ^ color[u]) != it.second) flag = true;
}
}
}
void solve(){
scanf("%d%d",&n,&m) ;
flag = false ;
for(int i = 0 ; i <= n ; i++) v[i].clear() , vis[i] = false , color[i] = 0 ;
for(int i = 0 ; i < m ; i++) {
int x , y ; char str[15] ;
scanf("%d%d%s",&x,&y,str);
if(str[0] == 'i') v[x].push_back({y,1}),v[y].push_back({x,1});
else v[x].push_back({y,0}),v[y].push_back({x,0});
}
int ans = 0 ;
for(int i = 1 ; i <= n ; i++) {
if(!vis[i]){
cnt0 = cnt1 = 0 ;
dfs(i) ;
ans += max(cnt0,cnt1);
}
}
printf("%d\n",flag ? -1 : ans);
}
int main()
{
//freopen("in","r",stdin);
scanf("%d",&t) ;
while (t--) solve() ;
return 0 ;
}
补充并查集做法:
//这里采用乱序合并,会生成 1 ~ 2 * n某些编号为根的树(树根的分布是1 ~ 2*n) , 其中某棵树 i 的敌对点 i + n 不一定是树根 , 所以对于每棵树,要找根对应敌对点所在根。
//也可以采用有序合并,开始就不太理解有序合并,想了很久才明白,也就是让某点为根的同时,让其敌对点也为根,这样查找效率更高,优化了常数。
#include <bits/stdc++.h>
using namespace std ;
const int N = 2e5+10 ;
int p[N*2] , cnt[N*2] ;
int t , n , m;
int a[N*5] , b[N*5] ;
bool enemy[N*5] ;
char str[15] ;
int fr(int x){
return x == p[x] ? p[x] : p[x] = fr(p[x]) ;
}
void merge(int x , int y)
{
x = fr(x) , y = fr(y) ;
if(x == y) return ;
p[x] = y ;
cnt[y] += cnt[x] , cnt[x] = 0;
}
int main()
{
// freopen("in","r",stdin);
scanf("%d",&t);
while (t--) {
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= n ; i++) p[i] = i , cnt[i] = 1 ;
for(int i = n + 1 ; i <= 2 * n ; i++) p[i] = i ,cnt[i] = 0;
for(int i = 1 ; i <= m ; i++){
scanf("%d%d%s",&a[i],&b[i],str);
if(str[0]=='i') enemy[i] = true ;
else enemy[i] = false ;
}
bool flag = false ;
for(int i = 1 ; i <= m ; i++) {
int x = fr(a[i]) , y = fr(b[i]);
int xe = fr(a[i] + n) , ye = fr(b[i] + n);
if(enemy[i]){
if(x == y || xe == ye) {
flag = true ;break;
}
merge(x , ye) ;
merge(xe , y) ;
}else{
if(x == ye || y == xe) {
flag = true ;break;
}
merge(x , y) ;
merge(xe , ye);
}
}
if(flag) {
puts("-1") ;continue;
}
int Sum = 0 ;
for(int i = 1 ; i <= n ; i++) {
if(p[i] == i) {
Sum += max(cnt[i],cnt[fr(i+n)]);
}
}
for(int i = n + 1 ; i <= 2 * n ; i++){
if(p[i] == i) {
Sum += max(cnt[i],cnt[fr(i-n)]);
}
}
printf("%d\n",Sum / 2);
}
return 0 ;
}