演算法:關押罪犯(二分圖判定,染色法)
判斷一個圖是否是二分圖
二分圖的概念:如果頂點C可以分割為兩個互不相交的子集,並且圖中每條邊(i, j)所關聯的兩個頂點i和j分別屬於這兩個不同的頂點集,則稱圖為一個二分圖。
奇數環的概念:一個環中邊的個數是奇數。
二分圖當且僅當圖中不含奇數環。
將圖中的每個結點染為1或2兩種顏色,如果遇到一條邊的兩個點的染色是相同的,則說明該圖不是一個二分圖。應該在主函式中加一個for迴圈遍歷每一個連通塊,然後用bfs遍歷連通塊中的每一個結點,使其染色,然後判斷是否矛盾,如果矛盾就返回false。
用二分來列舉怒氣值,當兩個人的矛盾小於怒氣值時,相當於這兩條邊不存在,在此基礎上判斷當前圖是不是二分圖。
問題描述
S 城現有兩座監獄,一共關押著 N 名罪犯,編號分別為1~N。
他們之間的關係自然也極不和諧。
很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。
我們用“怨氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。
如果兩名怨氣值為 c 的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為 c 的衝突事件。
每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成一個列表,然後上報到 S 城 Z 市長那裡。
公務繁忙的 Z 市長只會去看列表中的第一個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。
在詳細考察了 N 名罪犯間的矛盾關係後,警察局長覺得壓力巨大。
他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。
假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。那麼,應如何分配罪犯,才能使 Z 市長看到的那個衝突事件的影響力最小?這個最小值是多少?
輸入格式
第一行為兩個正整數 N 和 M,分別表示罪犯的數目以及存在仇恨的罪犯對數。
接下來的 M 行每行為三個正整數aj,bj,cj,表示aj號和bj號罪犯之間存在仇恨,其怨氣值為cj。
資料保證1≤aj<bj<N,0<cj≤1000000000 且每對罪犯組合只出現一次。
輸出格式
輸出共1行,為 Z 市長看到的那個衝突事件的影響力。
如果本年內監獄中未發生任何衝突事件,請輸出0。
資料範圍
N≤20000,M≤100000
輸入樣例:
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
輸出樣例:
3512
程式碼
#include <iostream>
#include <cstring>
using namespace std;
const int N = 20010, M = 200010;
int n, m;
int ne[M], e[M], h[N], w[M], idx = 0;
int color[N];
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs(int a, int b, int c){
color[a] = b;
for (int i = h[a]; i != -1; i = ne[i]){
if(w[i] <= c) continue;
int j = e[i];
if(!color[j]){
if(!dfs(j, 3-b, c)) return false;
}else if(color[j] == b) return false;
}
return true;
}
int main(){
memset(h, -1, sizeof h);
scanf("%d %d", &n, &m);
int a, b, c;
while (m--){
scanf("%d %d %d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
int l = 0, r = 1000000000;
while (l < r){
memset(color, 0, sizeof color);
int mid = l + r >> 1;
int flag = false;
for (int i = 1; i <= n; ++i){
if (!color[i]){
if (!dfs(i, 1, mid))
{
flag = true;
break;
}
}
}
if(!flag){
r = mid;
}else{
l = mid + 1;
}
}
cout << l << endl;
return 0;
}