题意:
给出一张图,每条边都有一个颜色(要么是红色要么是蓝色),如果翻转一个顶点,那么这个顶点相邻的边都会变色(红变蓝,蓝变红),求使得整个图变成同色的最小操作数,并且输出要翻转哪些顶点。
思路:最小操作数一定是全部变成红色和全部变成蓝色操作数的最小值,我们以全部变成红色举例:如果一条边是红色,那么这条边的两个顶点必然是:要么同时不翻转要么同时翻转,如果一条边是蓝色,那么这条边的两个顶点翻转的情况肯定是不一致的,我们不妨将翻转情况一致的放在同一个集合, 那么就会有两个集合,一个表示翻转的点一个表示不翻转的点。那么这个问题就变成图的染色问题,dfs或者bfs染色即可,因为集合可以通过翻转互补,一个连通块的最小操作数就是最小那个集合的尺寸。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct Edge{
int to, col, next;
}E[maxn << 1];
int head[maxn];
int vis[maxn], top, n, m;
vector < int > g[2], st[2];
int cnt=0;
void add_edge(int u, int v, int col){
E[cnt].to = v;
E[cnt].col = col;
E[cnt].next = head[u];
head[u] = cnt++;
}
bool dfs(int u, int f, int col){ //f的取值为1表示翻,0表示不翻
vis[u] = f;
st[f].push_back(u);
for(int i = head[u]; ~i; i = E[i].next){
int v = E[i].to;
if(vis[v] != -1)
{
if((vis[u] ^ vis[v]) != (col ^ E[i].col))
return false;
continue;
}
if(!dfs(v, col ^ E[i].col ^ vis[u], col))
return false;
}
return true;
}
int solve(int col){
memset(vis, -1, sizeof vis);
for(int i = 1; i <= n; i++){
if(vis[i] == -1){
st[0].clear();
st[1].clear();
if(!dfs(i, 0, col)){
return n + 1;
}
int tmp = st[0].size() < st[1].size() ? 0 : 1;
g[col].insert(g[col].end(), st[tmp].begin(), st[tmp].end());
}
}
return g[col].size();
}
int main(){
int u, v;
char col;
scanf("%d%d", &n, &m);
memset(head, -1, sizeof head);
for(int i = 1; i <= m; i++){
scanf("%d %d %c", &u, &v, &col);
add_edge(u, v, col == 'R' ? 1 : 0);
add_edge(v, u, col == 'R' ? 1 : 0);
}
int sz0 = solve(0);
int sz1 = solve(1);
if(sz0 == n + 1 && sz1 == n + 1){
puts("-1");
return 0;
}
int tmp = sz0 < sz1 ? 0 : 1;
printf("%d\n", g[tmp].size());
for(int i = 0; i < g[tmp].size(); i++){
printf("%d ", g[tmp][i]);
}
return 0;
}