题目描述
分析
二分出同一监狱允许的最大怨气值 m i d mid mid, 检查每一对罪犯间的怨气值 c c c
若 c ≤ m i d c≤mid c≤mid,则这一对罪犯可以在同一监狱,
若 c > m i d c > mid c>mid,则这一对罪犯必须分在不同的监狱.
将两座监狱看成二分图的左右两部分, 将仇恨关系看做边
则满足要求的分法组成的图一定是二分图.
我们可以使用染色法判断二分图
如果满足要求就继续二分 [ l e f t , m i d ] [left, mid] [left,mid],
因为此时右半区间 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right]一定满足二分图(相当于在原二分图基础上去掉一些边)
否则二分 [ m i d + 1 , r i g h t ] [mid+1, right] [mid+1,right], 从而得到最终的答案
也可参考Y总视频讲解
实现
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 2e4 + 9, M = 1e5 + 9;
struct node
{
int v, c; // 邻接点, 怨气值
};
vector<node> edge[M];
int n,m;
int color[N];
void add(int a, int b, int c)
{
edge[a].push_back(node{b,c});
}
bool dfs(int u, int Color, int mid)
{
color[u] = Color;
for(int i=0; i<edge[u].size(); i++)
{
int v = edge[u][i].v;
int c = edge[u][i].c;
if(c > mid) // 过滤掉 怨气值 <= mid 的边
{
if(color[v] == -1)
{
if(!dfs(v,!Color,mid)) return false; // 邻接点染成相反颜色
}
else if(color[v] == Color) return false; // 邻接点染相同颜色,则不是二分图
}
}
return true;
}
bool check(int mid) // 检查怨气值 > mid的点和边是否构成二分图
{
memset(color, -1, sizeof(color));
for(int i=1; i<=n; i++)
{
if(color[i] == -1)
{
if(!dfs(i,0,mid)) return false;
}
}
return true;
}
int main()
{
cin >> n >> m;
int a, b, c;
for(int i=0; i<m; i++)
{
cin >> a >> b >> c;
add(a,b,c);
add(b,a,c);
}
int left = 0, right = 1e9;
while(left < right)
{
int mid = left + right >> 1;
if(check(mid)) right = mid;
else left = mid + 1;
}
cout << left << endl;
return 0;
}