二分图辨别

本文深入探讨了二分图的概念,解释了其在图论中的特殊性质,即顶点可划分为两个互斥的独立集,使得每条边连接不同集合的顶点。文章还详细介绍了通过深度优先搜索算法在线性时间内判断一个图是否为二分图的方法,包括输出二着色或奇环的过程。

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

什么是二分图?

在图论中,二分图是一类特殊的图,因为它的顶点可以分成两个互斥的独立集的点集U,V(也就是所有的点可以分为两个集合,相同集合中的点互相没有边),使得所有边都是连结一个 U 中的点和一个 V 中的点。顶点集 U、V 被称为是图的两个部分。等价的,二分图可以被定义成图中所有的环都有偶数个顶点。
二分图单靠人眼好似挺难辨认的

如何辨别二分图

给定一个图,使用深度优先搜寻法,可以在线性时间内判断它是否为二分图,并输出一个二着色 (若答案为是),或是一个奇环 (若答案为否)。方法是先用深度优修搜寻法找出图的一个深度优先搜寻森林 (各连通部分取一个深度优先搜寻树),这是图的一个生成森林。接着运用树的搜寻将森林二着色,也就是依序各顶点着和它的父节点相异的颜色。现在检查原图中深度优先搜寻森林以外的其他边,如果所有边的两端点都不同颜色,则这也是原图的一个二着色,并且输出它;如果有一个边 e 的两端点相同颜色,则此两端点在同一个连通部分,也就是在森林的同一棵树上,找出在森林中,连接两端点的路径 P,因为 P 上的顶点的颜色是交错出现的,因此 P 有偶数个顶点,加上 e 就形成一个奇环,并且输出它。
事实上,在上述的算法中,深度优先搜寻森林只是作为一个生成森林,让我们来着色。因此,用不同的方式获得的别的生成森林仍然可以使算法可以运作,例如,可以用广度优先搜寻取出广度优先搜寻森林。事实上,我们并不需要真的再建一个森林,只用在搜索的时候进行染色再记录下每一个点的状况就好了,如果碰到一个点本来应该染上这个颜色却已经染上了另一个颜色(例如绿色),那么这个时候我们就可以大胆的做出决定抛弃它了。
代码(转自nymphy

#include<cstdio>
#include<vector>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5010;
const int Mod=1e9+7;
vector<int>G[maxn];
int x[maxn],y[maxn],vis[maxn],col[maxn],N;
int qpow(int a,int x){
    int res=1;
    while(x){
        if(x&1) res=(res*1ll*a)%Mod;
        x>>=1;
        a=(a*1ll*a)%Mod;
    }   return res;
}
void read(int &res){
    char c=getchar();res=0;
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar()) res=(res<<3)+(res<<1)+c-'0';
}
void init(int len){
    for(int i=1;i<=N;i++) vis[i]=0,G[i].clear();
    for(int i=1;i<=N;i++)
     for(int j=i+1;j<=N;j++){
          int dis=abs(x[i]-x[j])+abs(y[i]-y[j]);
          if(dis>len) {
              G[i].push_back(j);
              G[j].push_back(i);
          }
    }
}
bool dfs(int u,int opt)
{
    vis[u]=1; col[u]=opt; 
    int L=G[u].size();
    for(int i=0;i<L;i++){
        int v=G[u][i];
        if(!vis[v]) {
            if(!dfs(v,opt^1)) return false;
        }
        else if(col[v]==opt) return false;
    }
    return true;
}
bool check(int len)
{
    init(len);
    for(int i=1;i<=N;i++){
        if(!vis[i]) {
            if(!dfs(i,0)) return false;
        }
    }
    return true;
}
int main()
{
    read(N); 
    for(int i=1;i<=N;i++) read(x[i]),read(y[i]);
    int L=0,R=10000,Mid,ans;
    while(L<=R){
        Mid=(L+R)>>1;
        if(check(Mid)) ans=Mid,R=Mid-1;
        else L=Mid+1;
    }
    init(ans); int cnt=0,ans2;
    for(int i=1;i<=N;i++){
        if(!vis[i]){
            cnt++;
            dfs(i,0);
        }
    }
    ans2=qpow(2,cnt);
    printf("%d\n%d\n",ans,ans2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值