什么是二分图?
在图论中,二分图是一类特殊的图,因为它的顶点可以分成两个互斥的独立集的点集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;
}