染色法划分二分图 二分查找 T3 257. 关押罪犯 - AcWing题库

城现有两座监狱,一共关押着 名罪犯,编号分别为

他们之间的关系自然也极不和谐。

很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。

我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。

如果两名怨气值为 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 市长那里。

公务繁忙的 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了 名罪犯间的矛盾关系后,警察局长觉得压力巨大。

他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。

假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入格式

第一行为两个正整数 ,分别表示罪犯的数目以及存在仇恨的罪犯对数。

接下来的 行每行为三个正整数 ,表示 号和 号罪犯之间存在仇恨,其怨气值为

数据保证 且每对罪犯组合只出现一次。

输出格式

输出共 行,为 市长看到的那个冲突事件的影响力。

如果本年内监狱中未发生任何冲突事件,请输出

数据范围

输入样例:

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>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 2e4 + 10, M = 3e5 + 10;
int h[N], e[M], ne[M], w[M], idx;
int color[N];
int n,m;
 
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 mid, int c)
{
    color[u] = c;
    for(int i = h[u]; ~i ; i = ne[i])
    {
        int j = e[i];
        if(w[i] <= mid) continue;
        if(color[j])
        {
            if(color[j] == c) return false;
        }
        else if(!dfs(j, mid, 3 - c)) return false;
        
    }
    return true;
}
 
bool check(int mid)
{
    memset(color, 0, sizeof color);
    for(int  i = 1; i <= n; i++)
        if(!color[i])
        {
            if(!dfs(i, mid, 1)) return false;
        }
    return true;
}
 
 
int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while(m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a,b,c), add(b,a,c);
    }
    
    int l = -1, r = 1e9 + 1;
    while(l != r - 1)
    {
        int mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid;
    }
    if(r == 1e9 + 1) cout << 0 << endl;
    else cout << r << endl;
    
    return 0;
}