题目连接: 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;
}