TYVJ1403(NOIP2010提高组T3)关押罪犯

题目大意:
n个点,m条边,边权cj。将n个点分到两个集合中(集合均不能为空),使两个集合内的所有边的最大值最小,输出最小值。

样例输入:
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

样例输出:
3512

样例解释:
A集合:1 4;
B集合:2 3;
集合内边权最大值为3512.

数据范围:
对于100%的数据有N<=20000,M<=100000.//毕竟NOIP. %%%%%

解题思路:
NOIP2010……第一眼,这题怎么这么水!这题也叫NOIP?
我觉得我可以不开屏幕拿省队了,,上天上天。。
……
……
……
三小时后我觉得自己就是个烧饼……还有他也是@YOUSIKI
其实此题有两种解法:二分图染色or并查集。
在这里说一说二分图染色吧。
先把所有边按边权排个序,记录两个端点(struct),然后二分边的编号,由于要求最大边的最小值,显然我们从大到小加入边的这一行为是满足二分性质的(神犇zrt的教诲)。每次二分判断是否可行,只需将图01染色(同一条边的两个端点一个标0一个标1),出现矛盾说明不是二分图,即不能满足将这些点分到两个集合中,继续二分即可。
注:

  • 染色时dfs or bfs皆可,但一定要搞成bool型,以便及时返回,否则T一年。
  • 染色时不要忘了开father数组,防止在两个点之间来回走,再次T一年。
  • 存反向边。
  • 每次二分边的条数时重新存图,比直接存一个图再判断要快,为什么?不知道。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
struct N{
    int head;
    int ver;
    int val;
    friend bool operator < (N aaa,N bbb){
        return aaa.val>bbb.val;
    }
}aa[100005];
const int dian=20005;
const int bian=100005;
int n,m,ans;
int head[dian],next[bian],ver[bian],vis[dian];
bool dfs(int s,int fa){
    for(int i=head[s];i!=-1;i=next[i]){
        int x=ver[i];
        if(x==fa)
            continue;
        bool v=vis[s];
        if(vis[x]==-1){
            if(v){
                vis[x]=0;
                if(!dfs(x,s))
                    return false;
            }
            else{
                vis[x]=1;
                if(!dfs(x,s))
                    return false;
            }
        }
        else if(v==vis[x])
            return false;
    }
    return true;
}
bool pd(int a){
    int tot=0;
    memset(vis,-1,sizeof(vis));
    memset(next,-1,sizeof(next));
    memset(head,-1,sizeof(head));
    for(int i=1;i<=a;i++){
        tot++;
        ver[tot]=aa[i].ver;
        next[tot]=head[aa[i].head];
        head[aa[i].head]=tot;
        tot++;
        ver[tot]=aa[i].head;
        next[tot]=head[aa[i].ver];
        head[aa[i].ver]=tot;
    }
    for(int i=1;i<=n;i++){
        if(vis[i]==-1){
            vis[i]=0;
            if(!dfs(i,-1))
                return false;
        }
    }
    return true;
}
void ef(int l,int r){
    if(l>r){
        return;
    }
    int mid=(l+r)/2;
    if(pd(mid)){
        ef(mid+1,r);
    }
    else{
        ans=mid;
        ef(l,mid-1);
    }
}
int main(){
    int a,b,c,tot=0;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        aa[i].head=a;
        aa[i].ver=b;
        aa[i].val=c;
    }
    sort(aa+1,aa+m+1);
    ef(1,m);
    if(ans==m)
        printf("0");
    else
        printf("%d",aa[ans].val);
    return 0;
}

NOIP %%%%%

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值