题目大意
给出一个图,边有两种颜色,可以改变一个点相连边的颜色,都取反,求最少改变多少个点使得全部边颜色一样。
解题思路
可以发现一个点最多改变一次,由于只有两种颜色,一条边只和两个点有关,知道一个点必可求出另一个点的状态,可以暴力求解,具体怎么暴力可以看代码,注意可能有多个连通块。
code
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=100000,maxm=100000,inf=2147483647;
int n,m,gra,ans=inf,to[maxm*2+10],col[maxm*2+10],next[maxm*2+10],begin[maxn*2+10],tag[maxn+10],queue[maxn+10];
bool visit[maxn+10],done[maxn+10];
void insert(int u,int v,int c){
to[++gra]=v;
col[gra]=c;
next[gra]=begin[u];
begin[u]=gra;
}
int bfs(int s,int c,int t){
int head=0,tail=0,sum=c;
tag[s]=c;visit[queue[++tail]=s]=1;
for(;head!=tail;){
int now=queue[++head];
for(int i=begin[now];i;i=next[i])
if(!visit[to[i]]){
sum+=(tag[to[i]]=col[i]^t^tag[now]);
visit[queue[++tail]=to[i]]=1;
}else if(tag[to[i]]^col[i]^t^tag[now]){
sum=inf/2;
break;
}
if(sum>=inf/2)break;
}
fo(i,1,tail){
visit[queue[i]]=0;
done[queue[i]]=1;
}
return sum;
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m){
int u,v;char c;scanf("%d%d %c",&u,&v,&c);
insert(u,v,c=='B');insert(v,u,c=='B');
}
int tmp=0;
fo(i,1,n)
if(!done[i]){
tmp+=min(bfs(i,0,0),bfs(i,1,0));
if(tmp>=inf/2)break;
}
ans=min(ans,tmp);
memset(done,0,sizeof(done));
tmp=0;
fo(i,1,n)
if(!done[i]){
tmp+=min(bfs(i,0,1),bfs(i,1,1));
if(tmp>=inf/2)break;
}
ans=min(ans,tmp);
if(ans>=inf/2)printf("-1");
else printf("%d",ans);
return 0;
}