最坏情况,把所有罪犯关在同一个监狱,最大值就是1e9,最好情况,同一个监狱内部的两个点之间都是没有边的,答案为0,所以mid从0到1e9开始二分。
目标寻找最大冲突的最小值ans,我们来看一下是否具有二分性质:
看这个性质能不能把区间分成两段,使得最优解是两段的边界
如果我们把仇恨值大于mid的边都保留,把所有<= mid的边都删除的话,这个图是不是二分图,
在本题中,就是把所有仇恨值>mid的罪犯分到两个监狱里面,能否存在一种划分方案,使得所有仇恨值>mid的边的两个点都在不同的监狱里面。如果可以的话,说明仇恨值一定<=mid。
如果说mid取值更大,即在最优解右侧,也一定成立,因为最优解相当于把所有>mid的边都加进去了,它是一个二分图,那么我们从右边选点的话,相当于选择出来的边数变少了,一个二分图去掉一些边也是二分图。因此,右边的点都是满足这个性质的。
对于左边的点都是不满足这个性质的,如果左边存在一个点满足(把所有大于这个值的边分到两个监狱的话,可以成功的话),那么我们的最优解应该是这个点,就矛盾了,因此左边的这些点都是不满足的。因此这个性质是可以二分的。
二分之后要判断一下,所有>mid的边构成的图是不是二分图。
判断二分图,可以用染色法。如果成功的话,说明答案应该<=mid,否则答案应该> mid
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 20010, M = 200010;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
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 u, int c, int limit)
{
color[u] = c;
for (int i = h[u]; i != -1; i = ne[i])
{
if (w[i] <= limit) continue;
int j = e[i];
if (color[j])
{
if (color[j] == c) return false;
}
else if (!dfs(j, 3 - c, limit)) return false;
}
return true;
}
bool check(int limit)
{
memset(color, 0, sizeof color);
for (int i = 1; i <= n; ++ i)
if (color[i] == 0)
if (!dfs(i, 1, limit))
return false;
return true;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m --)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
int l = 0, r = 1e9;
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
return 0;
}