# 「NOIP2010」关押罪犯(二分图染色+二分答案)

「NOIP2010」关押罪犯(二分图染色+二分答案)

洛谷 P1525

  • 描述:n个罪犯(1-N),两个罪犯之间的仇恨值为c,m对仇恨值,求怎么分配使得两件监狱的最大仇恨值最小。

  • 思路:使最大xxx最小,描述就很二分。二分一个答案x后,对仇恨值大于x的罪犯之间构成的图进行二分图染色(相邻节点不染同一种颜色,总共两种颜色),染色成功则答案可行,复杂度\(O(nlogn)\)

    二分图染色:把每个未标记的节点标记为任意一种颜色,对其进行一次 BFS,将该节点所在的连通分支全部染色,每一次扩展把未被染色的相邻节点标记为与自身相反的颜色,如果发现扩展出去的节点的颜色与自身相同,则染色失败。

AcCode:

#include <bits/stdc++.h>
using namespace std;
#define fre freopen("C:\\Users\\22765\\Desktop\\in.txt","r",stdin);
#define ms(a) memset((a),0,sizeof(a))
#define re(i,a,b) for(register int (i)=(a);(i)<(b);++(i))
#define sf(x) scanf("%d",&(x))
#define rg register
typedef long long LL;
const int inf=(0x7f7f7f7f);
const int maxn=1e5+5;
int n;
struct node{
    int x,y,r;
    bool operator < (const node& a)const{
        return r>a.r;
    }
}s[maxn]; 
vector<int> v[maxn];
int vis[maxn];
bool isok;

//dfs() : 能否将x节点所在的连通分支染色成功 
bool dfs(int x,int color){
    
    if(!isok)return 0;//该连通分支染色失败,则图无法染成二分图
     
    vis[x]=color;
    int u;
    re(i,0,v[x].size()){
        u=v[x][i];
        if(vis[u]==vis[x]){
            isok=0;return 0;    
        }
        
        if(!vis[u]&&!dfs(u,3-color)){//若节点未被染色,且从该节点开始染色无法染色成功 
            isok=0;return 0; 
        }
    
    }
    return 1;
}
inline bool check(int m){
    ms(vis);isok=1;
    re(i,1,n+1)v[i].clear();
    
    re(i,0,m){
        if(s[i].r>m)//对仇恨值大于二分答案的节点建图 
            v[s[i].x].push_back(s[i].y),v[s[i].y].push_back(s[i].x);
        
        else break;
    }
    
    re(i,1,n+1){
        //这里一遍dfs可能无法将全部节点染色,可能有多个连通分支,所以遍历每个点 
        if(!vis[i]&&!dfs(i,1))return 0;
    }
    return 1;
}
int main(){
    sf(n);
    int m;sf(m);
    re(i,0,m)scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].r);
    sort(s,s+m);//排序预处理便于后面建图 
//  cout<<s[0].r<<endl;
    int l=0,r=s[0].r,mid;
    while(l<r){
        mid=l+(r-l)/2;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    cout<<l<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sstealer/p/11289841.html
今日推荐