【NOIP 2010】关押罪犯 虚点并查集

CODEVS:http://codevs.cn/problem/1069/
题目描述 Description
S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极
不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是少?
输入描述 Input Description
第一行为两个正整数N 和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。
接下来的M 行每行为三个正整数aj,bj,cj,表示aj 号和bj 号罪犯之间存在仇恨,其怨气值为cj。数据保证,且每对罪犯组合只出现一次。
输出描述 Output Description
共1 行,为Z 市长看到的那个冲突事件的影响力。如果本年内监狱
中未发生任何冲突事件,请输出0。
样例输入 Sample Input

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

样例输出 Sample Output

3512

数据范围及提示 Data Size & Hint
罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件

影响力是3512(由2 号和3 号罪犯引发)。其他任何分法都不会比这个分法更优。

【数据范围】

对于30%的数据有N≤ 15。

对于70%的数据有N≤ 2000,M≤ 50000。

对于100%的数据有N≤ 20000,M≤ 100000。

题解
这个题目我似乎还是考完NOIP之后在AC了,我太弱了,原来一直知道怎么写但是一直没有写,现在再出来写解题报告给学弟学妹们看算是造福一中的OIER了吧。
这个题目第一眼看上去要让爆发的怒气值最小于是就想到了二分【我会补上二分写这个题的题解的】,但是这个题目我们有更优时间复杂度的解法:并查集。思路也蛮简单就是尽量把怒气值很大的那几对囚犯分开来放,然后在判断会不会发生冲突,诶,似乎和最小生成树的思路很像啊。
但是仔细想一想,实现的时候有一点问题,我们并查集只能把罪犯放在一起啊,怎么把罪犯分开来放呢?这儿一般有两种处理方法

  1. 虚点并查集
  2. 带权并查集

本题解中我们使用简单易懂但是时间、空间复查度的常数稍微大一点带来的虚点并查集。
虚点并查集思路如下:
20160503235720859.png

我们多开了一倍的数组,用A+N表示A的敌人、B的朋友
B+N表示A的朋友、B的敌人
每次把A和B分离的操作改为,将A和B+n合并,B和A+n合并。
也就是A和A的朋友、B的敌人(B+N)合并,B和B的朋友、A的敌人(B+N)合并。这样只最终每次合并的时候直接判断A和B是否在一个数组就可以了。
代码:

/*
作者:WZH
题目:p1069 关押罪犯
*/

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <map>
const int maxn=400005;
using namespace std;
long long n,m,father[maxn],sum,rank[maxn],y;
struct node
{
    friend bool operator < (const node &a,const node &b)
     {
         if(a.c<b.c) return true;
          return false;
     }
    long long u;
    long long v;
    long long c;
}head[maxn],x;
priority_queue<node>q;
long long getfather(int x)
{
    if(father[x]==x) return x;
    else father[x]=getfather(father[x]);
    return father[x];
}
void read_column_number()
{
    scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++)
     {
    scanf("%d%d%d",&head[i].u,&head[i].v,&head[i].c);
     q.push(head[i]);
     }
    
}
void Union(int x,int y)
{
    int fx=getfather(x);
    int fy=getfather(y);
     if(rank[fx]>rank[fy])
      {
          father[fy]=fx;
          rank[fx]+=rank[fy];
      }
     else
      {
          father[fx]=fy;
          rank[fy]+=rank[fx];
      }
}
void Kruskal()
{
    while(!q.empty())
      {
          x=q.top();q.pop();
          if(getfather(x.u)!=getfather(x.v))
            Union(x.u,x.v+n),Union(x.v,x.u+n);
          else { cout<<x.c;return; }
      }
     cout<<"0"; 
}
int main()
{
    read_column_number();
    for(int i=1;i<=2*n;i++)
    father[i]=i,rank[i]=1;
    Kruskal();
    return 0;
}
Last modification:October 1st, 2018 at 09:54 am
If you think my article is useful to you, please feel free to appreciate

Leave a Comment