1. 程式人生 > 實用技巧 >二分圖判定

二分圖判定

二分圖判定

定義

簡而言之,就是頂點集V可分割為兩個互不相交的子集,並且圖中每條邊依附的兩個頂點都分屬於這兩個互不相交的子集,兩個子集內的頂點不相鄰。

——摘自度娘

還是不懂的可以查閱百度百科,上面有例子。

判定

在一張無向圖中,若圖中不存在長度為奇數的環,則該圖是二分圖。

簡單證明:

若存在長度為奇數的環,環中任意一點為A,屬於集合1,則環內與A相鄰的兩個頂點B,C都會被分到與A不同的集合2中。然後其中一個點的相鄰的點C(還有一點是A就不用考慮了)就也分到集合1中,若A,B,C(可能有更多點參與,情況與這樣類似)形成一個長度為奇數的環,則會發生衝突,因為A,C在一個集合, 不滿足定義。若僅僅是一個長度為偶數的環,也是可以的,若下圖所示。

分成集合A(SetA)與集合B(SetB)。

可以使用染色法來判斷二分圖。
給相鄰的點染上不同的顏色,看是否會發生衝突(與相鄰的點顏色相同)。

C++程式碼

bool Paint(int now, int color) {
	bool flag = true;
	col[now] = color;
	int SIZ = v[now].size();
	for(int i = 0; i < SIZ; i++) {
		int next = v[now][i];
		if(!col[next])
			flag &= Paint(next, 3 - color);
		else if(col[next] == color)
			return false;
	}
	return flag;
}
bool Build() {
	bool flag = true;
	memset(col, 0, sizeof(col));
	for(int i = 1; i <= n; i++)
		if(!col[i])
			flag &= Paint(i, 1);
	return flag;
}

附上一道簡單的題

NOIP2010 關押罪犯

題目描述

S 城現有兩座監獄,一共關押著\(N\)名罪犯,編號分別為\(1-N\)。他們之間的關係自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。我們用“怨氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為\(c\)的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為\(c\)的衝突事件。

每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成一個列表,然後上報到 S 城 Z 市長那裡。公務繁忙的 Z 市長只會去看列表中的第一個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。

在詳細考察了\(N\)名罪犯間的矛盾關係後,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。

那麼,應如何分配罪犯,才能使 Z 市長看到的那個衝突事件的影響力最小?這個最小值是多少?

輸入格式
每行中兩個數之間用一個空格隔開。第一行為兩個正整數\(N,M\),分別表示罪犯的數目以及存在仇恨的罪犯對數。接下來的\(M\)行每行為三個正整數\(a_j,b_j,c_j\),表示\(a_j\)號和\(b_j\)號罪犯之間存在仇恨,其怨氣值為\(c_j\)。資料保證\(1<a_j\leq b_j\leq N, 0 < c_j\leq 10^9\),且每對罪犯組合只出現一次。

輸出格式

\(1\)行,為 Z 市長看到的那個衝突事件的影響力。如果本年內監獄中未發生任何衝突事件,請輸出 0。

輸入輸出樣例

輸入

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

輸出

3512

說明/提示

【輸入輸出樣例說明】罪犯之間的怨氣值如下面左圖所示,右圖所示為罪犯的分配方法,市長看到的衝突事件影響力是 35123512(由 22 號和 33 號罪犯引發)。其他任何分法都不會比這個分法更優。

【資料範圍】

對於\(30\%\)的資料有\(N\leq 15\)

對於\(70\%\)的資料有\(N\leq 2000,M\leq 50000\)

對於\(100\%\)的資料有\(N\leq 20000,M\leq 100000\)

思路

可以二分答案。
對於大於mid的邊,刪除該邊,判斷是否是二分圖。
如果是,那麼mid就可以為答案。否則就不是。

程式碼

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
void Quick_Read(int &N) {
	N = 0;
	char c = getchar();
	int op = 1;
	while(c < '0' || c > '9') {
		if(c == '-')
			op = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		N = (N << 1) + (N << 3) + c - 48;
		c = getchar();
	}
	N *= op;
}
const int MAXN = 2e4 + 5;
struct Node {
	int To, Hate;
	Node() { To = Hate = 0; }
	Node(int T, int H) {
		To = T;
		Hate = H; 
	}
};
vector<Node> v[MAXN];
int n, m;
int col[MAXN];
void Read() {
	int A, B, C;
	Quick_Read(n);
	Quick_Read(m);
	for(int i = 1; i <= m; i++) {
		Quick_Read(A);
		Quick_Read(B);
		Quick_Read(C);
		v[A].push_back(Node(B, C));
		v[B].push_back(Node(A, C));
	}
}
bool Paint(int now, int color, int maxn) {
	bool flag = true;
	col[now] = color;
	int SIZ = v[now].size();
	for(int i = 0; i < SIZ; i++) {
		if(v[now][i].Hate <= maxn)
			continue;
		int next = v[now][i].To;
		if(!col[next])
			flag &= Paint(next, 3 - color, maxn);
		else if(col[next] == color)
			return false;
	}
	return flag;
}
bool Build(int maxn) {
	bool flag = true;
	memset(col, 0, sizeof(col));
	for(int i = 1; i <= n; i++)
		if(!col[i])
			flag &= Paint(i, 1, maxn);
	return flag;
}
void Dich() {
	int l = 0, r = 1e9;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(Build(mid))
			r = mid;
		else
			l = mid + 1;
	}
	printf("%d", l);
}
int main() {
	Read();
	Dich();
	return 0;
}