思路: 并查集 + 优先队列
优先队列貌似也用不上,排序就可以了。
样例输入
2 1
1 2 28135
样例输出0
没有通过: 再次提醒我,注意边界。注意极端情况
刚开始,往二分图上想了
即将一个无向图分成两个独立的点集,使两个点集中所有的边的边权的最大值的最小值是多少(然而我不会二分图染色……)
扫盲: 什么叫做二分图,二分图的最大匹配
http://www.renfei.org/blog/bipartite-matching.html
二分图的最大匹配如果换一个说法:最多有多少互相喜欢的男孩/女孩可以配对儿?这就是最大匹配问题。
/*
输入
4 4
1 2 100000
3 4 100000
1 3 2
2 4 1
输出
0
*/
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int MM=20001;
int N,M; // N 罪犯数目 M 仇恨对数
struct Hen{
int jia;
int yi;
int cost;
bool operator < ( const Hen &another) const {
if( cost >= another.cost ) return true;
else return false;
}
};
int father[MM];
int rank[MM];
Hen hens[100001];
int find(int x){
if( father[x] == x ) return x;
else {
return father[x] = find( father[x] );
}
}
void unite(int x,int y){
int genx=find(x);
int geny=find(y);
if( genx != geny ) {
if( rank[genx] > rank[geny] ){
father[geny] = genx;
}
else {
father[genx] = geny;
if(rank[genx]==rank[geny]){ rank[geny]++ ;}
}
}
}
bool same(int x ,int y){
return ( find(x) == find(y) );
}
int main()
{
cin >> N >> M;
for(int i=0;i<=N ;i++){ //注意这里的边界,考场中哪有这么都样例让你试呢。
father[i]=i;
rank[i] = 1;
}
// priority_queue<Hen> pq;
for(int i=1;i<=M;i++){
int a ,b ,c; cin >> a >> b >>c;
//pq.push( (Hen){a,b,c} );
hens[i]=(Hen){a,b,c};
}
//排序就可以了!
sort(hens+1 , hens+M+1);
int result=0;
/*while(!pq.empty()){
Hen k = pq.top(); pq.pop();
if( same(k.jia, k.yi) ){
result = k.cost;
break;
}
else{
unite( k.jia, k.yi );
}
}*/
for(int i=1;i<=M;i++){
Hen k = hens[i] ;
if( same( k.jia, k.yi ) ){
result = k.cost;
break;
}
else{
unite( k.jia, k.yi );
}
}
cout << result;
return 0;
}
只得了60分。苦苦思考。
在网上找到了答案:
/*
输入
4 4
1 2 100000
3 4 100000
1 3 2
2 4 1
输出
0
*/
我的代码的结果是1
网上找到了答案: 摘录如下:
第一个点集:1、4;第二个点集:2、3。这样,就不会产生任何事件,输出为0,但是,用刚才的思路会输出1。为什么呢?让我们来模拟一下。首先,让1、2在同一并查集;然后,让3、4在同一并查集;之后,让1、3在同一并查集,此时两个并查集合并。最后,再判断2、4时,便会得到两数在同一并查集,于是就输出了1。那么,这个错误就得到一个解释了:因为2、4的距离是奇数,所以即使它们同在一个并查集集合中,它们也不一定同在一个监狱。所以,距离问题也是一个需要考虑的问题。
似乎只能暴力维护了,但是这样又很可能TLE。那怎么办呢?这时,我们就要从前一个思路所产生的错误着手了。它产生错误的原因是:不能维护两点之间的距离。那么,我们可以引入一个新的节点(或者思路):补集。我们设置另外一个节点系统:i+n。凡是与i+n节点在同一个并查集的,都是不能与i在同一个集合里。这样,因为i+n这个节点世界上是不存在的,所以保证了每一个并查集集合中的存在的点之间的距离都是偶数。这样就保证了一旦两个点在同一个集合,则它们必须在同一个点集中。另外,注意对i+n也要压缩路径,因为有可能有两个补集节点相连,这时如果不find就会造成一大堆东西连在一起,就会MLE(具体怎么搞得我也不知道)。
然后记得数组要开2*n,然后用一个优先队列维护一下怒气值,就搞定了。
其实代码很简单,但是思路还是很巧妙的。还有一些细节的东西(也没多少),具体请见代码:完美全部通过落谷数据
/*
输入
4 4
1 2 100000
3 4 100000
1 3 2
2 4 1
输出
0
*/
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int MM=40002;
int N,M; // N 罪犯数目 M 仇恨对数
struct Hen{
int jia; int yi; int cost;
bool operator < ( const Hen &another) const {
if( cost >= another.cost ) return true;
else return false;
}
};
int father[MM];
int rank[MM];
Hen hens[100001];
int find(int x){
if( father[x] == x ) return x;
else {
return father[x] = find( father[x] );
}
}
void unite(int x,int y){
int genx=find(x);
int geny=find(y);
if( genx != geny ) {
if( rank[genx] > rank[geny] ){
father[geny] = genx;
}
else {
father[genx] = geny;
if(rank[genx]==rank[geny]){ rank[geny]++ ;}
}
}
}
bool same(int x ,int y){
return ( find(x) == find(y) );
}
int main()
{
cin >> N >> M;
for(int i=0;i<N*2 ;i++){ //注意这里的边界,考场中哪有这么都样例让你试呢。
father[i]=i;
rank[i] = 1;
}
// priority_queue<Hen> pq;
for(int i=1;i<=M;i++){
int a ,b ,c; cin >> a >> b >>c;
//pq.push( (Hen){a,b,c} );
hens[i]=(Hen){a-1,b-1,c}; //转化成 0 到 n-1
}
//排序就可以了!
sort(hens+1 , hens+M+1);
int result=0;
/*while(!pq.empty()){
Hen k = pq.top(); pq.pop();
if( same(k.jia, k.yi) ){
result = k.cost;
break;
}
else{
unite( k.jia, k.yi );
}
}*/
for(int i=1;i<=M;i++){
Hen k = hens[i] ;
if( same( k.jia, k.yi ) ){
result = k.cost;
break;
}
else{
unite( k.jia, k.yi + N );
unite( k.jia+N, k.yi );
}
}
cout << result;
return 0;
}