NOIP2010关押罪犯[并查集|二分答案+二分图]

题目描述

S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

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

输入输出格式

输入格式:

输入文件的每行中两个数之间用一个空格隔开。第一行为两个正整数N 和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的M 行每行为三个正整数aj,bj,cj,表示aj 号和bj 号罪犯之间存在仇恨,其怨气值为cj。数据保证1<aj=<=bj<=N ,0 < cj≤ 1,000,000,000,且每对罪犯组合只出现一次。

输出格式:

共1 行,为Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出0。

输入输出样例

输入样例#1:

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

输出样例#1:

3512

说明

【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是3512(由2 号和3 号罪犯引发)。其他任何分法都不会比这个分法更优。

【数据范围】对于30%的数据有N≤ 15。对于70%的数据有N≤ 2000,M≤ 50000。对于100%的数据有N≤ 20000,M≤ 100000。

--------------------------------------------------------------

并查集:

i的敌人集合为i+n,同一个敌人集合的人一定在同一个监狱

按怒气从大到小排序,依次考虑每对敌人,如果在同一个集合就输出,负责合并到对方的敌人集合里(因为是一个人的敌人)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20005,M=100005;
int n,m,a,b,c,ans=0;
struct edge{
    int u,v,w;
}e[M];
int cnt=0;
inline void add(int u,int v,int w){
    cnt++;
    e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;e[cnt].u=u;
}
inline bool cmp(edge a,edge b){
    return a.w>b.w;
}
int fa[N<<1];
inline int findp(int x){
    return x==fa[x]?x:fa[x]=findp(fa[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    sort(e+1,e+1+m,cmp);
    for(int i=1;i<=n*2;i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        int u=e[i].u,v=e[i].v,w=e[i].w;
        int f1=findp(u),f2=findp(v);
        if(f1==f2){ans=w;break;}
        fa[f1]=findp(v+n);
        fa[f2]=findp(u+n);
    }
    printf("%d",ans);
} 

二分图:

二分最小c值,二染色看能不能成立,两种二染色方法

不知道为什么tle两个。。。。。原来是忘记m<<1了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20005,M=100005;
int n,m,a,b,c,ans=0;
struct edge{
    int v,w,ne;
}e[M<<1];
int cnt=0,h[N];
inline void ins(int u,int v,int w){
    cnt++;
    e[cnt].v=v;e[cnt].w=w;e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].w=w;e[cnt].ne=h[v];h[v]=cnt;
}
int col[N],W,w[M];
bool bi(int u){//printf("%d %d\n",u,col[u]);
    for(int i=h[u];i;i=e[i].ne){
        if(e[i].w<=W) continue;
        int v=e[i].v;
        if(col[u]==col[v]) return false;
        if(!col[v]){
            col[v]=3-col[u];
            if(!bi(v)) return false;
        }
    }
    return true;
}
bool dfs(int u,int fa){
    for(int i=h[u];i;i=e[i].ne){
        if(e[i].w<=W) continue;
        int v=e[i].v;
        if(v==fa) continue;
        if(col[u]==col[v]) return false;
        if(!col[v]){
            col[v]=3-col[u];
            if(!dfs(v,u)) return false;
        }
    }
    return true;
}
bool check(){
    for(int i=1;i<=n;i++)
        if(!col[i]){
            col[i]=1;
            if(!bi(i)) return false;
        }
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        ins(a,b,c);w[i]=c;
    }
    sort(w+1,w+1+m);
    int l=1,r=m;
    while(l<r){
        int m=(l+r)/2; W=w[m];//printf("m %d %d\n",m,w[m]);
        memset(col,0,sizeof(col));
        if(check()) ans=w[m],r=m;
        else l=m+1;
    }
    if(l==1&&r<l) cout<<0;
    else printf("%d",ans);
}
时间: 2024-10-13 16:16:58

NOIP2010关押罪犯[并查集|二分答案+二分图]的相关文章

[noip2010]关押罪犯 并查集

第一次看的时候想到了并查集,但是不知道怎么实现: 标解,f[i]表示i所属的集合,用f[i+n]表示i所属集合的补集,实现的很巧妙,可以当成一个使用并查集的范例: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<map> 6 #include<ctime> 7 #include<vector>

洛谷P1525 关押罪犯 并查集

无冲突 输出 0 洛谷P1525 关押罪犯 并查集 用拆点法 将一个点拆成两份 一个点和 x 的朋友相连 一个点和 x的敌人相连 若 x 与 y 是敌人 因为只有两个阵营 所以满足敌人的敌人就是朋友 然后 x 连向 y 的敌人 y 连向 x 的敌人 因为这是双向边 所以 y的朋友就是x的敌人就不用连了 如果某一时刻 getfather(e[ i ].from)==getfather(e[ i ].to) 这说明两个点已经是敌人关系的两个人 通过朋友关系连上了 那就结束了 1 #include <

HDU 3081 Marriage Match II(网络流+并查集+二分答案)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3081 题目: Problem Description Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. W

洛谷P1525关押罪犯——并查集

题目:https://www.luogu.org/problemnew/show/P1525 并查集+贪心,从大到小排序,将二人分在不同房间,找到第一个不满足的即为答案. 代码如下: #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,fa[20005],ans,ct,d[20005]; struct N{ int hd,to,w; }edge[1000

codevs 1069 关押罪犯 并查集

很有意思的一道题.刚开始看不出这是道并查集,后来看了题解才会做,就是把要分开的囚犯放在同个集合里,表示这两个囚犯不在同一间监狱,但不是直接合并,例如1和2要分开,则1和2’.1’和2合并(x’为x的虚点),同个集合中实点和虚点分属于两间监狱(即x’与y在不同监狱):再例如,1和2,2和3要分开,则1和2’合并,2’和3合并,那么1和3会在同一间监狱,1’和2合并,2和3’合并,那么2就在另一间监狱.这样一来整道题的思路就呼之欲出了:按仇恨值从大到小分开每对囚犯,若出现无法分开的情况(即已在同一个

P1525 关押罪犯 并查集

题目链接:https://www.luogu.org/problemnew/show/P1525 一道很难想到思路的题,还可以用二分图染色法,但我们只考虑并查集 要使得冲突最小,首先我们应该让冲突大的尽可能的不在同一个监狱 如果两个相互冲突的人都与第三个人冲突,那么应优先让冲突大的在不同的监狱 那么冲突小的冲突就是不可避免地冲突 如果整个过程都按照冲突的大小从大到小进行的话第一个不可避免的冲突就是将会发生的冲突中最大的一个 用并查集来实现的话,就是将敌人的敌人与自己放在同一个集合中,在同一个集合

HDU 3277 Marriage Match III(并查集+二分答案+最大流SAP)拆点,经典

Marriage Match III Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1581    Accepted Submission(s): 464 Problem Description Presumably, you all have known the question of stable marriage match.

noip2010(关押罪犯)

4844: Noip2010 关押罪犯 Time Limit: 4 Sec  Memory Limit: 128 MBSubmit: 18  Solved: 12[Submit][Status][Web Board] Description S城现有两座监狱,一共关押着 N名罪犯,编号分别为 1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至 积怨已久,如果客观条件具备则随时可能爆发冲突.我们用"怨气值" (一个正整数值)来表示某两名罪犯之间 的仇恨程度,怨气值越大,则这两名罪犯之

并查集+二分 旅行

题目描述: A国有n座城市,每座城市都十分美,这使得A国的民众们非常喜欢旅行.然而,A国的交通十分落后,这里只有m条双向的道路,并且这些道路都十分崎岖,有的甚至还是山路,只能靠步行.通过每条道路的长度.泥泞程度等因素,我们给每条道路评估一个"崎岖度",表示通过这条道路的不舒适程度. 从X城市经过若干条道路到达Y城市,我们称这次旅行的"代价"为所经过道路"崎岖度"的最大值.当然,如果从X城市到Y城市有多条路线,民众们会自觉选择"代价&qu