【解题报告】 HDU 1102 Constructing Roads -- 并查集 最小生成树 Prime算法

题目连接: HDU 1102
题目大意:自己看。代码写的很长,主要想练二叉堆来实现优先队列,毕竟学了就得用上。
// HDU 1102 Constructing Roads --  并查集 最小生成树 Prime算法
// 二叉堆 -- 用于优先队列的实现
// 完全二叉树的 I 结点的 两个 child 是 I*2 and I*2+1 .
// 完全二叉树的 I 结点的 father 是 I/2 .
// 队列结构 -> head -> queue -> bottom -> 
//
#include <cstdio>
#include <iostream>
using namespace std;

const int MAXV = 105;
const int MAXVIA = 10005;
struct VIA{
	int cost,a,b;
}prority_queue[MAXVIA];
int head; // 只需要队列入口的指向   
int father[MAXV]; // 村庄的father
int num[MAXV];  // 记录集合中元素的个数
int cost[MAXV][MAXV];
bool flag[MAXV]; // 标记一个村庄是否 入队过了,实质是记录方向

void swap(VIA& a,VIA& b){
	a.a^=b.a^=a.a^=b.a;
	a.b^=b.b^=a.b^=b.b;
	a.cost^=b.cost^=a.cost^=b.cost;
}

bool is_empty(){
	return !(head - 1);
}

void InQueue(int cost,int a,int b){
	int node = head;
	int father = node /2 ; // /2
	prority_queue[head].a = a;
	prority_queue[head].b = b;
	prority_queue[head++].cost = cost;

	while(prority_queue[node].cost < prority_queue[father].cost){
		swap(prority_queue[node], prority_queue[father]);
		node = father;
		father = node /2 ; // /2
	}
	 // ensure right child bigger than left child, impossible!!
}

VIA DeQueue(){
	VIA val = prority_queue[1]; // the bottom of queue 
	int node = 1;
	VIA temp_head = prority_queue[--head]; // let the head element out queue
	while(node*2 < head){
		int temp_node = node; // save father node
		node = node << 1; // *2
		if (node+1 == head || prority_queue[node].cost < prority_queue[node+1].cost) // choose the smaller rise
			swap(prority_queue[node],prority_queue[temp_node]);
		else{
			swap(prority_queue[node+1],prority_queue[temp_node]);
			node = node + 1;
		}
	}
	prority_queue[node] = temp_head; // make the original element into the blank site
	int father = node /2 ; // /2  adjust the element to a appropriate site
	while(prority_queue[node].cost < prority_queue[father].cost){
		swap(prority_queue[node], prority_queue[father]);
		node = father;
		father = node /2 ; // /2
	}
	prority_queue[head].cost = 0;
	prority_queue[head].a = 0;
	prority_queue[head].b = 0;

	return val;
}

void init(int n){
	head = 1; // queue start with '1'
	for (int i = 0 ;i < MAXVIA ; i++){
		prority_queue[i].a = 0;
		prority_queue[i].b = 0;
		prority_queue[i].cost = 0;
	}
	memset(cost,0,sizeof(cost));
	memset(father,-1,sizeof(father));
	memset(flag,true,sizeof(flag));
	for (int i=0;i<MAXV;num[i]=1,i++);
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= n; j++){
			scanf("%d",&cost[i][j]);
			cost[j][i] = cost[i][j]; 
		}
	}
}

int find (int x){
	if (father[x] <= 0) return x;
	return father[x] = find(father[x]);
}

void combination(int a,int b){
	father[b] = a;
	if (father[a]<0)father[a] = 0;
	num[a] += num[b];
}

int Prime_MST(int n){ // 类似于广搜
	int sum = 0;
	for (int i = 2; i <= n; i++){ // 第一层入队
		int ra = find(i);
		int rb = find(1);
		if (ra != rb)
			InQueue(cost[i][1], i, 1); // 只有不在一个集合中才 入队候选
	}
	flag[1] = false;
	while( !is_empty() ){
		VIA temp;
		temp = DeQueue();
		int ra = find(temp.a);
		int rb = find(temp.b);
		if (ra != rb){
			sum += temp.cost;
			combination(ra,rb);
			if (num[ra]==n) return sum; // 当存在一个集合中的元素个数等于villages总数时
			int in;
			if (flag[temp.a]) in = temp.a; // 判断方向
			else in = temp.b;
			for (int i = 1; i <= n; i++){ // 下一层入队
				int rra = find(in); 
				int rrb = find(i);
				if (rra != rrb)
					InQueue(cost[i][in],i,in); // 只有不在一个集合中才 入队候选
			}
		}
	}

	return sum;
}

int main()
{
//	freopen("in.txt","r",stdin);
	int n_villages;
	while(scanf("%d",&n_villages) != EOF){
		init(n_villages); // 价格表
		int n_conn,a,b;
		scanf("%d",&n_conn);
		for (int i = 1;i <= n_conn; i++){
			scanf("%d %d",&a,&b);
			int ra = find(a);
			int rb = find(b);
			if (ra != rb){
				//combination(ra,rb);
				cost[a][b]=0;cost[b][a]=0; //如果连通把价格设为 0
			}
		} // 计算预算
		cout << Prime_MST(n_villages) << endl;
	}
	return 0;
}

你可能感兴趣的:(【解题报告】 HDU 1102 Constructing Roads -- 并查集 最小生成树 Prime算法)