Graph Coloring【NOIP2016提高A组模拟7.20】

本文介绍了一种图论中的染色问题,旨在寻找使无向图所有边颜色一致的最少步骤。通过分析不同颜色边之间的关系,利用广度优先搜索算法解决01图染色问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

现在你有一张无向图包含n个节点m条边。最初,每一条边都是蓝色或者红色。每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。
找到一种步数最小的方案,使得所有边的颜色相同。

样例输入:
第一行包含两个数n,m分别代表节点数和边的数量
接下来m行描述边,第i行ui,vi,ci,代表ui有一条颜色为ci的边与vi相连(ci是B或者是R),B代表蓝色,R代表红色。数据保证没有自环的边。
3 3
1 2 B
3 1 R
3 2 B

样例输出:
如果没有方案就输出-1。否则第一行输出k代表最小的步数。
1

数据范围:
对于30%数据,n<=20,m<=20
100%:1<=n,m<=100000


剖解题目:

……………………


思路:

画个图,捣鼓两种颜色中对于两个点的关系。


解法:

可以发现一个点最多只能被染一次,染了两次等于没染。
那么我们只需要分别求出都染成的红色和染成蓝色两种方案,选最优即可。
假设我们选择全部染成红色,那么对于一条红色的边,如果一端选择染色,那么另一端也必须要染色,所以这两个点必须归于一个集合。相反,对于蓝色的边,如果一端染色,那么另一端必定不能染色,这两个点便不能归于一个集合。
若一个点发生了冲突,那么这种情况下无解。
这道题就转化成了一个01图染色的问题。解决这种问题,自然就是运用BFS或DFS了,考虑道这道题数据有达到了105,采用BFS可防止辣鸡电脑爆栈。
注意整个图不一定每个点都是联通的!
这打了我130行+,虽说有一半几乎是复制的。我旁边的童鞋用Pascal打了5000b+,我也是醉了。


代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=100005;
int n,m,fre[maxn],next[maxn*2],go[maxn*2],color[maxn*2],num,bz[maxn];

void add(int x,int y,int z)
{
    go[++num]=y;
    next[num]=fre[x];
    fre[x]=num;
    color[num]=z;
}
int getred()
{
    bool bk[maxn];
    fo(i,1,n){
        bz[i]=-1;
        bk[i]=0;
    }
    int S=0,T=0,ans=0;
    fo(p,1,n)
    if (!bk[p]){
        bk[p]=true;
        bz[p]=0;
        S=1;T=0;
        int h=0,t=1,d[2*maxn+5];
        d[1]=p;
        while (h!=t){
            h=h%(2*maxn+5)+1;
            int u=d[h];
            int i=fre[u];
            while (i){
                if (bz[go[i]]==-1){
                    if (color[i]==82) {
                        if (bz[u]==0) ++S;
                        else ++T;
                        bz[go[i]]=bz[u];
                    }
                    else{
                        bz[go[i]]=(bz[u]^1);
                        if (bz[u]==0) ++T;
                        else ++S;
                    } 
                    t=t%(2*maxn+5)+1;
                    d[t]=go[i];
                    bk[go[i]]=true;
                }
                else{
                    if (bz[go[i]]==bz[u]&&color[i]==66) return -1;
                    if (bz[go[i]]!=bz[u]&&color[i]==82) return -1; 
                }
                i=next[i];
            }
        }
        ans=ans+min(S,T);
    } 
    return ans;
}
int getblue()
{
    bool bk[maxn];
    fo(i,1,n){
        bz[i]=-1;
        bk[i]=0;
    }
    int S=0,T=0,ans=0; 
    fo(p,1,n)
    if (!bk[p]){
        bk[p]=true;
        bz[p]=0;
        S=1;T=0;
        int h=0,t=1,d[2*maxn+5];
        d[1]=p;
        while (h!=t){
            h=h%(2*maxn+5)+1;
            int u=d[h];
            int i=fre[u];
            while (i){
                if (bz[go[i]]==-1){
                    if (color[i]==66) {
                        if (bz[u]==0) ++S;
                        else ++T;
                        bz[go[i]]=bz[u];
                    }
                    else{
                        bz[go[i]]=(bz[u]^1);
                        if (bz[u]==0) ++T;
                        else ++S;
                    } 
                    t=t%(2*maxn+5)+1;
                    d[t]=go[i];
                    bk[go[i]]=true;
                }
                else{
                    if (bz[go[i]]==bz[u]&&color[i]==82) return -1;
                    if (bz[go[i]]!=bz[u]&&color[i]==66) return -1; 
                }
                i=next[i];
            }
        }
        ans=ans+min(S,T);
    } 
    return ans;
}
int main()
{
    freopen("T2.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,m){
        int x,y,colour;
        scanf("%d%d",&x,&y);
        colour=getchar();
        colour=getchar();
        add(x,y,colour);
        add(y,x,colour);
    }
    int ans1=getred(),
        ans2=getblue();
    if (ans1==-1) printf("%d",ans2);
    else if (ans2==-1) printf("%d",ans1);
    else printf("%d",min(ans1,ans2));
    fclose(stdin);
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值