您的位置:首页 > 其它

codevs 1069 关押罪犯

2017-03-06 15:15 176 查看
codevs 1069 关押罪犯

本来想着只需要用sort把这对犯人按怨气值排序即可,然后从大到小把犯人放在不同的监狱里即可,后来才返现我忽视了一个重要问题,比如: a-b c-d ,接下来是b - c , b - d,就是c, d 仓库的方法对后面是有影响的。现在就发现,这个问题我似乎又不知道怎么去思考好了。考虑图形的环状吗?可是maxN = 20000。

看了题解,发现最好的方法是用并查集做(怎么自己就完全没有想到这个算法呢?太嫩了啊- - )

代码:

#include<iostream>
#include<algorithm>
using namespace std;

int fa[55555];

struct S {
int a, b, c;
friend inline bool operator < (S a, S b) {
return a.c > b.c;
}
} dat[100000];

int find(int x) {
return fa[x] == x ? x : (fa[x] = find(fa[x]));
}

int main() {
int n, m;
cin >> n >> m;
for (int i(0); i < m; ++i)
cin >> dat[i].a >> dat[i].b >> dat[i].c;
sort(dat, dat + m);
for (int i(0); i < 2 * n; ++i)
fa[i] = i;
for (int i(0); i < m; ++i) {
int fx(find(dat[i].a)), fy(find(dat[i].b));
if (fx == fy) {
cout << dat[i].c << endl;
return 0;
}
fa[fx] = find(dat[i].b + n);
fa[fy] = find(dat[i].a + n);
}
cout << 0 << endl;
return 0;
}


解释:

用并查集表示每个犯人之间的关系,在同一个集合中则说明两人在同一个监狱,反之则不在同一个监狱。用类似于克鲁斯卡尔的方法,先将边排序,然后按照仇恨值的大小从大到小处理。对于每一组,先看两个人能不能加入不同的集合,

如果可以,将两人加入不同的集合,如果不可以,则输出该组仇恨值。

用补集来表示两个点不在一个集合中。如a和b’在同一个集合中,则a和b不在同一个集合中,不能把b直接加入另一个集合,因为会对后面的结果产生影响。

如,3和4不在同一个集合中,但是我们现在不知道究竟是3在第一个监狱还是4在第一个监狱,所以此时用3和4‘在同一个集合中来表示3和4不在同一个集合中。——转载至http://blog.csdn.net/u013653310/article/details/46792373

自己的理解:(中午躺在床上半天终于想出来了),首先注意到有这条语句

int fx(find(dat[i].a)), fy(find(dat[i].b));


说明fa[x]里面的x可能是(0 ~n,即本身),也可能是(x + n,即它的补集),所以先认清楚一个思维误区,一个集合里面可能有(a+n , e+n,…),他们的联通块编号可能是某个数字,比如(e+n)。在同一个集合里面,所有的正常数字不能和里面的某个补集,比如a+n共存,也就说,所有数字不能和a放在同一个集合。那么a + n 和 e+n 都在的话,说明什么呢?我们可以这样想:既然所有数字都不能和a 一起, 也不能和e一起,而且又只有2个监狱,那么a 只能和 e一起。在这份代码中,只要某2个数字得到的联通快编号一样,说明它们在同一个集合中,就不能再在同一个监狱中,所以就是不合法的了。

终于想通了~总算懂了补集这个东西。每天进步一点点,万岁!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: