Codeforces 663C Graph Coloring(图染色dfs)

题意:

给出一张图,每条边都有一个颜色(要么是红色要么是蓝色),如果翻转一个顶点,那么这个顶点相邻的边都会变色(红变蓝,蓝变红),求使得整个图变成同色的最小操作数,并且输出要翻转哪些顶点。


思路:最小操作数一定是全部变成红色和全部变成蓝色操作数的最小值,我们以全部变成红色举例:如果一条边是红色,那么这条边的两个顶点必然是:要么同时不翻转要么同时翻转,如果一条边是蓝色,那么这条边的两个顶点翻转的情况肯定是不一致的,我们不妨将翻转情况一致的放在同一个集合, 那么就会有两个集合,一个表示翻转的点一个表示不翻转的点。那么这个问题就变成图的染色问题,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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值