题解:P1525 [NOIP2010 提高组] 关押罪犯

题意:对于一个图,把点分成两个集合,删去跨集合的边后让最大边权最小。
看到这里可以明显的想到二分图。

二分图

定义:
二分图,又称二部图,英文名叫 Bipartite graph。指节点由两个集合组成,且两个集合内部没有边的图

观察发现,与本题的要求刚好相反,那么自然可以想到:

现在我们知道,要判断一个答案是否合法,只要把大于答案的边连起来,判断是否为一个二分图
介绍一个 dfs 判断二分图的方法:

bool dfs(LL v, LL c){
    color[v] = c;    //将当前顶点涂色
    for(int i = 0; i < n; i++){    //遍历所有相邻顶点,即连着的点
        if(edge[v][i] == 1){    //如果顶点存在
            if(color[i] == c)    //如果颜色重复,就返回false
                return false;
            if(color[i] == 0 && !dfs(i,-c))    //如果还未涂色,就染上相反的颜色-c,并dfs这个顶点,进入下一层
                return false;   //返回false
        }
    }
    return true;   //如果所有顶点涂完色,并且没有出现同色的相邻顶点,就返回true
}

至于答案的话,采用二分答案的方法:

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>e[i].x>>e[i].y>>e[i].w;//按边存,方便排序
    }
    sort(e+1,e+m+1);
    LL l=0,r=m,mid;
    while(l<r-1){
        mid=(l+r)/2;
        if(check(mid))r=mid;
        else l=mid;
    }
    if(m==1) cout<<0;//特判
    else cout<<e[r].w;
    return 0;
}

代码

怕大家看不懂,参考了 @FY0123 大佬的代码重写了一遍,自己的码风不太好。

#include<bits/stdc++.h>
using namespace std;
using LL=long long;
LL n,m,col[500005];
bool flag;
struct node{
    LL x,y,w;
    bool operator < (const node &p)const{
        return p.w>w;
    }
}e[500005];
vector<LL> v[500005];
void dfs(LL x,LL c){
    col[x]=c;
    for(auto i : v[x]){
        if(!col[i]) dfs(i,c^1);
        else if(col[i]==c) flag=0;
    }
}
bool check(LL x){
    for(int i=1;i<=n;i++) v[i].clear();
    memset(col,0,sizeof(col));flag=1;
    for(int i=x+1;i<=m;i++){
        v[e[i].x].emplace_back(e[i].y);
        v[e[i].y].emplace_back(e[i].x);
    }
    for(int i=1;i<=n;i++){
        if(!col[i]){
            dfs(i,0);
            if(!flag) return 0;
        }
    }
    return 1;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>e[i].x>>e[i].y>>e[i].w;
    }
    sort(e+1,e+m+1);;
    LL l=0,r=m,mid;
    while(l<r-1){
        mid=(l+r)/2;
        if(check(mid))r=mid;
        else l=mid;
    }
    if(m==1) cout<<0;//特判
    else cout<<e[r].w;
    return 0;
}