原题链接:https://www.acwing.com/problem/content/1145/
把那些要求提前加进去的边,先合在一起,看成一个整体,然后再做一遍kruskal。 缩点!
但是,最后实现代码的时候,我们不一样非要去实现缩点操作。思路到代码之间还有一个转换的过程。
先用并查集加完,然后每个集合中只有一个代表元素了。这就相当于缩点了。用祖宗节点代表整个缩点之后的结果。
缩点过程被隐藏在了这个并查集find函数之中。
所以说由上我们可以看出来,这个kruskal非常牛逼啊。把所有边进行从小到大的偶排序之后,只做后一半也是对的。
其实它只做前边,也是对的,相当于求最小生成森林:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2010, M = 10010;
int n, m;
struct Edge
{
int a, b, w;
bool operator< (const Edge &t) const
{
return w < t.w;
}
}e[M];
int p[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m;
for(int i=1; i<=n; i++) p[i] = i;
int res = 0, k = 0;
for(int i=0; i<m; i++)
{
int t, a, b, w;
cin >> t >> a >> b >> w;
if(t == 1)
{
res += w; //直接加
p[find(a)] = find(b); //缩点
}
else e[k++] = {a, b, w};
}
sort(e, e+k);
for(int i=0; i<k; i++)
{
int a=find(e[i].a), b=find(e[i].b), w=e[i].w;
if(a != b)
{
p[a] = b;
res += w;
}
}
cout << res <<endl;
return 0;
}