Kruskal算法常常与prim算法放到一起讲,通常求最小生成树,本题求最小森林。本文只讲解Kruskal算法
具体参考学习网址:Kruskal详解
首先一棵生成树它是树,所以满足边是点数n-1,且生成树之间没有回路。
克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,
然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,
就可以作为最小生成树的一部分;
反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。
筛选出来的边和所有的顶点构成此连通网的最小生成树。
判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。
以此图为例
对所有边的权值大小进行升序排序,那么顺序(u,v, w)
(1,3,2)(2,3,3)(3,4,3)(1,2,5)(3,6,6)
(1,4,7)(5,6,7)(4,6,8)(3,5,9)(2,5,11)
但是kruskal则是从最小的边开始遍历所以1,3连接,然后是2 3和3 4,因为
两个顶点标记不同,即是说不是一个祖宗,所以可以相连。然后是(3,6)
因为3和6也是不同家族,所以可以相连。再看(1,4)和(5,6)因为(1,4)
已经属于一个家庭,所以不可以再连接,舍去,而5和6来自不同家庭,所以可以相连
现在发现边数恰等于点数-1,所以已经构成一棵树,故输出。
例题:
输入
第一行两个数n,m,q。
n代表图中点的数目,m代表边的数目,q代表查询的次数。
其中10<=n<=1000,10<=m<=200000,1<=q<=50
接下来m行,每行三个数u,v,w。
代表点u和v之间的权重为w (−50≤w≤50)
接下来q行,每行一个整数s。
输出
对于每次查询,输出s所在连通分量的最小生成树的权重和。
输入:
10 20 1
9 3 53
10 3 78
1 4 3
8 7 10
2 8 10
10 9 98
6 3 100
9 7 70
4 10 26
7 10 25
5 2 25
7 4 5
5 7 29
6 1 24
8 1 87
3 1 78
3 8 64
6 8 34
3 2 86
2 10 78
5
输出:
219
限制
1s, 10240KiB for each test case
因为不允许用STL,所以代码很冗长
#include<iostream>
using namespace std;
template<class T>
void changeLength1D(T*& a, int oldLength, int newLength)
{
if (newLength < 0)
exit(1);
T* temp = new T[newLength]; // new array
int number = min(oldLength, newLength); // number to copy
copy(a, a + number, temp);
delete [] a; // deallocate old memory
a = temp;
}
//小根堆
template<class T>
class minHeap{
public:
minHeap(int initialCapacity){
heap=new T[initialCapacity+1];
arrayLength=initialCapacity+1;
heapSize=0;
}
int size(){return heapSize;}
void push(const T& theElement);
bool empty(){return heapSize==0;}
T top(){return heap[1];}
void pop();
void initialize(T * theHeap,int theSize);
void deactivateArray(){
heap=NULL;
arrayLength=heapSize=0;
}
private:
T *heap;// number of elements in queue
int arrayLength;// queue capacity + 1
int heapSize; // element array
};
template<class T>
void minHeap<T>::initialize(T *theHeap, int theSize){
heap=theHeap;
heapSize=theSize;
arrayLength=heapSize+1;
for(int root=heapSize/2;root>=1;root--){
T rootElement=heap[root];
int child=2*root;
while(child<=heapSize){
if(child<heapSize&&heap[child].weight>heap[child+1].weight)
child++;
if(rootElement.weight<=heap[child].weight)
break;
heap[child/2]=heap[child];
child*=2;
}
heap[child/2]=rootElement;
}
}
template<class T>
void minHeap<T>::push(const T &theElement){
// Add theElement to heap.
// increase array length if necessary
if(heapSize==arrayLength-1){
// double array length
changeLength1D(heap, arrayLength, 2 * arrayLength);
arrayLength *= 2;
}
// find place for theElement
// currentNode starts at new leaf and moves up tree
int currentNode=++heapSize;
while(currentNode!=1&&heap[currentNode/2].weight>theElement.weight){
heap[currentNode]=heap[currentNode/2];
currentNode/=2;
}
heap[currentNode]=theElement;
}
template<class T>
void minHeap<T>::pop(){
// Delete min element
heap[1].~T();
// Remove last element and reheapify
T lastElement=heap[heapSize--];
// find place for lastElement starting at root
int currentNode=1,child=2;
while(child<=heapSize){
// heap[child] should be smaller child of currentNode
if(child<heapSize&&heap[child].weight>heap[child+1].weight)
child++;
// can we put lastElement in heap[currentNode]?
if(lastElement.weight<=heap[child].weight)
break;
heap[currentNode]=heap[child];
currentNode=child;
child*=2;
}
heap[currentNode]=lastElement;
}
int Find(int *parent,int theElement){
return parent[theElement]==theElement?theElement:parent[theElement]=Find(parent, parent[theElement]);
}
struct Edge{
int start;
int end;
int weight;
Edge(){start=0;end=0;weight=0;}
Edge(int start,int end,int weight){
this->start=start;
this->end=end;
this->weight=weight;
}
};
int main(){
int n,m,q;
cin>>n>>m>>q;
minHeap<Edge> mH(m);
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
Edge e(u,v,w);
mH.push(e);
}
int *pa=new int[n+1];//父亲数组
int *length=new int[n+1];
for(int i=0;i<n+1;i++){
pa[i]=i;
length[i]=0;
}
int k=0;//k 记录已经连接了多少条边
for(int i=0;i<m;i++){
if(k==n-1)
break;
Edge e=mH.top();//获取堆顶元素
mH.pop();//删除第一个最小数
int root1=Find(pa, e.start);//从根开始寻找父节点
int root2=Find(pa, e.end);//查找
if(root1!=root2){
k++;
pa[root2]=root1;
length[root1]+=(length[root2]+e.weight);
length[root2]=length[root1];
}
}
for(int i=0;i<q;i++){
int s;
cin>>s;
cout<<length[Find(pa, s)]<<endl;
}
return 0;
}