题目传送门
一、二分图着色原理
二、dfs深度优先遍历解法
#include <bits/stdc++.h>
using namespace std;
//dfs代码简短一些,注意第29行。
const int N = 1e4 + 10; //题目中说结点数最大10^4=1e4
const int M = 1e5 * 2 + 10; //边数上限,因为是无向图,所以记录两次,就是1e5*2
//链式前向星
struct Edge {
int to, next;
} e[M];
int head[N], idx;
void add(int u, int v) {
e[++idx].to = v;
e[idx].next = head[u];
head[u] = idx;
}
int n, m, ans, ans1, ans2;
int color[N]; //上色结果
/**
* 功能:对二分图进行上色
* @param u 要上色的结点
* @param c 上一个过来结点的颜色
*/
void dfs(int u, int c) {
//上一个结点颜色是1,那么u就是2
if (c == 1) color[u] = 2, ans2++;
//上一个结点颜色是2,那么u就是1
else if (c == 2) color[u] = 1, ans1++;
//遍历每个出边
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
//如果已上色
if (color[v] > 0) {
//颜色与上一个结点的颜色一样,那么就是冲突
if (color[v] == color[u]) {
printf("Impossible");
exit(0);
}
}//未上色,则进行上色即可
else dfs(v, color[u]);
}
}
int main() {
cin >> n >> m;
int x, y;
for (int i = 1; i <= m; ++i) {
cin >> x >> y;
add(x, y), add(y, x);//无向图,双向创建
}
//寻找每个结点,如果没有染过颜色,就深度优先开始染色
for (int i = 1; i <= n; ++i)
if (!color[i]) {
//每一轮用之前清空为零
ans1 = ans2 = 0;
dfs(i, 1);//假设第一个结点是颜色1
//两种颜色哪个少哪个是答案
ans += min(ans1, ans2);
}
//如果有解,那么输出
printf("%d", ans);
return 0;
}
三、bfs广度优先遍历解法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10; //题目中说结点数最大10^4=1e4
const int M = 1e5 * 2 + 10; //边数上限,因为是无向图,所以记录两次,就是1e5*2
/*
根据题意可知二分图染色。若无法被染成二分图,输出Impossible;
反之,对于每个二分图,记录两种颜色的点的个数,取min后记录答案中。
注意,图可能不连通。因此对于每个二分图,都要进行取min操作,而不是对整个图染色后取min。
*/
int color[N]; //0:未上色,1:黑色,2:白色
int n, m, ans;
//广度优先搜索的队列
queue<int> q;
//链式前向星
struct Edge {
int to, next;
} edge[M];
int idx, head[N];
void add(int u, int v) {
edge[++idx].to = v;
edge[idx].next = head[u];
head[u] = idx;
}
//广度优先搜索
int bfs(int a) {
int ans1 = 0, ans2 = 0;
color[a] = 1;
ans1++;
q.push(a);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
//目标点未着色
if (color[v] == 0) {
//来源点是黑的,那么目标点是白的
if (color[u] == 1) color[v] = 2, ans2++;
//来源点是白的,那么目标点是黑的
else if (color[u] == 2) color[v] = 1, ans1++;
q.push(v);
} else
//如果已着色,那么需要判断和现在的要求是不是有冲突:
if (color[v] == color[u]) {//相临的存在冲突
printf("Impossible");
exit(0);
}
}
}
//哪个小就用哪个
return min(ans1, ans2);
}
int main() {
//n个结点,m条边
cin >> n >> m;
//m条边
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
add(x, y), add(y, x); //无向图双向建边,题目中明显说是无向图!
}
//注意,图可能不连通。因此对于每个二分图,都要进行取min操作,
//而不是对整个图染色后取min。
//这里有一个小技巧,就是遍历每个结点,如果此结点没有上过色,就表示是一个独立的图,需要单独计算
for (int i = 1; i <= n; ++i)
if (!color[i]) ans += bfs(i);
//输出
printf("%d", ans);
return 0;
}