版权声明:~~~感谢支持! https://blog.csdn.net/qq_39897867/article/details/88847231
题目
Description
现在你有一张无向图包含n个节点m条边。最初,每一条边都是蓝色或者红色。每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。
找到一种步数最小的方案,使得所有边的颜色相同。
Input
第一行包含两个数n,m(1<=n,m<=100000)分别代表节点数和边的数量
接下来m行描述边,第i行ui,vi,ci,代表ui有一条颜色为ci的边与vi相连(ci是B或者是R),B代表蓝色,R代表红色。数据保证没有自环的边。
Output
如果没有方案就输出-1。否则第一行输出k代表最小的步数
解题思路
总体思想是分别检查将边变为蓝色,或者红色,然后取最优方案即可。
每一个点最多改变一次,因为改变两次等于没有变化。因此我们要将点分为两
个集合 S 和 T,分别代表要改变的点和不需要改变的点。如果我们现在要将所有边变
为红色,假设 u 和 v 之间有一条红色的边。如果要维持这种颜色,u 和 v 要属于同一个
集合(S 或者 T)。另一方面,如果这条边是蓝色,那么 u 和 v 就必须在两个不同的集
合。
于是问题简化为了 0-1 图染色问题,我们可以通过 DFS 或者 BFS 解决。因为图
不一定连通,所以有一些细节要注意。
代码
#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#define rep(i,x,y) for (register int i=x;i<=y;i++)
using namespace std;
const int nm=100100;
struct node{int y,z,next;}a[nm*2];
int tot,n,m,fa[nm],d[nm],ans=2147483647,cnt,head[nm];
queue<int> q;
void add(int x,int y,int z){
a[++tot]=(node){y,z,head[x]}; head[x]=tot;
a[++tot]=(node){x,z,head[y]}; head[y]=tot;
}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int bfs(int s,int la){
while (!q.empty()) q.pop();
q.push(s); d[s]=1; int ant=1,r=1;
while (!q.empty()) {
int x=q.front(); q.pop();
for (register int i=head[x];i;i=a[i].next){
int y=a[i].y;
if (d[y]!=2){
if (la==a[i].z&&d[x]!=d[y]||la!=a[i].z&&d[x]==d[y]) return -1;
} else {
d[y]=(d[x]+la+a[i].z)%2;
if (d[y]==1) ant++;
q.push(y); r++;
}
}
}
if (r-ant<ant) ant=r-ant;
return ant;
}
int main(){
scanf("%d%d",&n,&m);
int x,y; char c;
rep(i,1,n) fa[i]=i;
rep(i,1,m)
scanf("%d%d",&x,&y),cin>>c,fa[find(x)]=find(y),add(x,y,(c=='R')?1:0);
rep(i,0,1) {
cnt=0;
rep(j,1,n) d[j]=2;
rep(j,1,n) if (fa[j]==j) {
int g=bfs(j,i);
if (g==-1) {cnt=-1; break;}
cnt+=g;
}
if (cnt>-1&&cnt<ans) ans=cnt;
}
if (ans==2147483647) printf("-1"); else printf("%d",ans);
}