【NOIP2010TG/codevs1069】 关押罪犯 解题报告

关押罪犯  NOIP2010TG/codevs1069钻石Diamond

题目描述 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。

【解题思路】

并查集,,然而老师讲的是搜索,,并没有听懂,,懂了之后补上。。

首先,把数据按照怒气值排序;

按照怒气值从大到小扫,如果两个罪犯属于一个集,那么就直接输出最大值,结束程序;(等会再说为什么)

下面来说一说这道题和传统的并查集不同的地方(感谢大(you)宝(wei)的帮助!):

要明白的是,一共只有两个监狱,所以假如说A和B不在同一个监狱,B和C不在同一个监狱,那么A和C一定在同一个监狱;

明白了这点就好说了\(^o^)/~;;;;

要设一个g数组(并查集的数组是f),从大到小扫的时候,比如说扫到了一组数据a,b,c(a,b是两个罪犯,c是怒气值),如果a,b不在一个集合里,就是说不冲突,那么我们就要判断,如果g[a]=0,那么g[a]=b,也就是存一下两个罪犯的关系,如果g[a]!=0,那么把g[a]与b合并,为下组做准备,然后同理处理g[b]。。其实这个过程就是上面让大家理解的ABC的关系;

在一个集合里就表示在一个监狱里;

g数组的初值就是0就可以了;

如果实在不明白看代码理解一下吧,大宝讲了之后我也不是很明白,写着写着就想通了。。

【代码】

#include
#include
#include
#include
using namespace std;
int f[20005],g[20005];
struct hp{
	int x,y;
	int num;
}angry[100005];
int n,m;

bool cmp(hp a,hp b)
{
	return a.num>b.num;
}

int find(int x)//并查集的查找元素(路径压缩)
{
	if (f[x]!=x)
	  f[x]=find(f[x]);
	return f[x];
}

void merge(int x,int y)//合并两个集合
{
	int a=find(x);
	int b=find(y);
	f[a]=b;
}

int main()
{
	freopen("prison.in","r",stdin);
	freopen("prison.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	  scanf("%d%d%d",&angry[i].x,&angry[i].y,&angry[i].num);
	sort(angry+1,angry+1+m,cmp);
	for (int i=1;i<=n;i++)
	{
		f[i]=i;
		g[i]=0;
	}
	for (int i=1;i<=m;i++)
	  {
	  	if (find(angry[i].x)==find(angry[i].y))//如果与前面冲突,那么直接输出,此时就为怒气最大的最小值
	  	  {
	  	  	printf("%d",angry[i].num);
	  	  	return 0;
	  	  }
	  	if (g[angry[i].x]==0)//这就是上面所说的处理ABC的关系
	  		g[angry[i].x]=angry[i].y;
	  	else
	  	  merge(g[angry[i].x],angry[i].y);
	  	if (g[angry[i].y]==0)
	  		g[angry[i].y]=angry[i].x;
	  	else
	  	  merge(g[angry[i].y],angry[i].x);
	  }
	printf("0");
	return 0;
}
【心得】

考试的时候用的搜索,结果是华丽丽的TLE,话说貌似二分+DFS是可以过的。

你可能感兴趣的:(题解,并查集,NOIP)