「学习笔记」二分图基础
二分图
二分图又称作二部图,是图论的一种特殊模型。设 \(G = (V,E)\) 是一个无向图,如果顶点 \(w\) 可以分割成两个互不相交的子集 \((A,B)\),且图中的每一条边 \((i,j)\) 所关联的两个定点分别属于这两个不同的顶点集 \((i \in A, j \in B)\),则称图 \(G\) 为一个二分图。
通俗一点说就是一个图的点分为两部分,每一部分之间的点没有边相连,这就是二分图。
如图:
判断
染色法
给定一张图,把里面的点染为黑白两色,根据给出的边依次进行染色(初始的点没有颜色),如把第一条边的起点染为黑色,那么把与之相连的未染色点染为白色,如果已经染色为白色则跳过,如果已经染为黑色则说明此图不是二分图,全部染完后把终点更新成起点继续染色,直到出现要进行染色的终点与起点颜色相同时返回假,表示不是二分图,或者所有的点染色成功返回真,表示此图为二分图。
int dfs(int u, bool col) {
if(used[u]) {
// 如果该点已被染色,判断是否与它相邻的节点颜色不同,即是否与col相同
if(color[u] == col) return 1;// 是返回1
else return 0;// 否则返回0
}
used[u] = true;// 没用过,标记为已用过
color[u] = col;// 染色
sum[col]++;// 该种颜色的总数++,即颜色为col的集合的点+1
int fg = 1;// 初始化fg为1
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
fg = (fg && dfs(v, col^1));//判断由它出发的其他节点是否能染色成功
}
return fg;
}
例题:P1330 封锁阳光大学
这就是一个判断是否为二分图的简单题目,用 dfs 深搜来判断染色,再处理一下答案就好了
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long ll;
using namespace std;
const int N = 1e4+5;
const int M = 1e5+5;
int n, m, cnt;
ll ans;
int h[N];
int color[N], used[N];
ll sum[2];
struct edge {
int u, v, nxt;
} e[M<<1];
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while(ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
void add(int u, int v) {
e[++cnt].u = u;
e[cnt].v = v;
e[cnt].nxt = h[u];
h[u] = cnt;
}
int dfs(int u, bool col) {
if(used[u]) {
if(color[u] == col) return 1;
else return 0;
}
used[u] = true;
color[u] = col;
sum[col]++;
int fg = 1;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
fg = (fg && dfs(v, col^1));
}
return fg;
}
int main() {
memset(color,0,sizeof color);
n=read(), m=read();
for(int i = 1; i <= m; ++i) {
int x = read(), y = read();
add(x, y);
add(y, x);
}
for(int i = 1; i <= n; ++i) {
if(used[i]) continue;
sum[0] = sum[1] = 0;
if(!dfs(i, 0)) {
printf("Impossible\n");
return 0;
}
ans += min(sum[0], sum[1]);
}
printf("%lld\n", ans);
return 0;
}
朝气蓬勃 后生可畏