P1525 关押罪犯

两种解法都解决了。。。

这道题有两种解法。

第一种是并查集补集,就是开两倍并查集空间,要隔离,就把一人的原集和另一个人的补集合并。

从大到小排序,直到无法合并的时候就输出,这个时候就是答案。

我之前做(chao)过,这里就不写了。

第二种是二分答案+二分图判定。

显然,答案希望是越来越小,因为被隔离的人会更多。

但是可能会冲突,即不满足两个监狱。需要更多的监狱来装他们。

把这些人隔离在两个监狱,就是把点隔离在两个集合里面。

这个东西就是二分图的模型。

只有那些矛盾值大于答案的,才需要隔离,才有边。

所以在这个基础上,进行二分图判定(染色法),确实是二分图就是一个可行解。

这里讲一下二分图判定的一个小细节:

  • 这个二分图不一定连通。所以每次找到点color为0时就从这个点开始染色。确保整个图都染了。

  • 当两边颜色一样的时候直接return false。

二分的时候有点小操作,自己领悟去吧。。。

然后就可以了。

代码:

#include<cstdio>
#include<cstring>
#include<queue>

const int maxn = 20005, maxm = 100005;
struct Edges
{
    int next, to, weight;
} e[maxm << 1];
int head[maxn], tot;
int n, m;
int color[maxn];
void link(int u, int v, int w)
{
    e[++tot] = (Edges){head[u], v, w};
    head[u] = tot;
}
bool check(int mid)
{
    memset(color, 0, sizeof color);
    std::queue<int> q;
    for(int x = 1; x <= n; x++)
    {
        if(color[x] == 0)
        {
            color[x] = 1; q.push(x);
            while(!q.empty())
            {
                int u = q.front(); q.pop();
                for(int i = head[u]; i; i = e[i].next)
                {
                    int v = e[i].to;
                    if(e[i].weight <= mid) continue;
                    if(color[v] == color[u]) return false;
                    else if(color[v] == 0)
                    {
                        color[v] = 3 - color[u];
                        q.push(v);
                    }
                }
            }
        }
    }
    return true;
}
int main()
{
    scanf("%d%d", &n, &m);
    while(m--)
    {
        int u, v, w; scanf("%d%d%d", &u, &v, &w);
        link(u, v, w); link(v, u, w);
    }
    int left = 0, right = 1000000005, ans = 0;
    while(left <= right)
    {
        int mid = (left + right) / 2;
        if(check(mid)) ans = mid, right = mid - 1;
        else left = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2018-10-12 21:52  Garen-Wang  阅读(129)  评论(0编辑  收藏  举报