题目:
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
- 这题主要考查最小生成树算法(Prime算法和Kruskal算法)
解决代码
#include<stdio.h>
#include<stdlib.h>
#define MAXN 1001 //从1开始
#define INF 100000
int Map[MAXN][MAXN],Cost[MAXN],N,M;
int parent[MAXN]; //用于并查集存放父亲下标
typedef struct Edge //边节点
{
int V;
int W;
int Cost;
}Edge;
Edge *E;
void CreateGraph(int n, int e) //创建初始化图
{
int v,w,i;
for(v=1;v<=n;v++)
{
Cost[v]=0; //存放各顶点到最小生成树的距离(价格)
for(w=1;w<=n;w++)
{
if(v==w)
Map[v][w]=0; //自己到自己不要钱
else
Map[v][w]=INF;
}
}
E=(Edge*)malloc(sizeof(struct Edge)*e); //开一个边的动态数组
for(i=0;i<e;i++)
{
scanf("%d %d %d",&E[i].V,&E[i].W,&E[i].Cost); //赋值边
Map[E[i].V][E[i].W]=E[i].Cost; //邻接矩阵权重赋值
Map[E[i].W][E[i].V]=E[i].Cost;
}
}
/*************************************Prime算法******************************************/
int FindMinCost() //找最小价格
{
int i,MinCost,MinCostPosition;
MinCost=INF;
MinCostPosition=0;
for(i=1;i<=N;i++)
{
if(Cost[i] && Cost[i]<MinCost) //如果价格不等于0,且更小
{
MinCost=Cost[i];
MinCostPosition=i;
}
}
return MinCostPosition;
}
int Prime()
{
int i,GrossCost=0,MinCost,Count=0;
for(i=1;i<=N;i++)Cost[i]=Map[1][i]; //从第1个村出发到各村的价格
while(1)
{
MinCost=FindMinCost();
if(MinCost==0)break; //找不到最小的价格
else //找到最小价格,最小生成树长大
{
GrossCost+=Cost[MinCost]; //总价格更新
Count++; //计数器更新
Cost[MinCost]=0;
for(i=2;i<=N;i++)
{
if(Cost[i] && Cost[i]>Map[MinCost][i]) //如果价格不等于0,且各还没收入顶点到更新后的最小生成树距离更小
{
Cost[i]=Map[MinCost][i]; //那么更新距离
}
}
}
}
if(Count<N-1)return -1;
else return GrossCost;
}
/*************************************Kruskal算法******************************************/
void Shell_Sort() //希尔排序
{
int Si, D, P, i;
Edge Tmp;
int Sedgewick[] = {3905,2161,929, 505, 209, 109, 41, 19, 5, 1, 0};
for ( Si=0; Sedgewick[Si]>=M; Si++ ) ;
for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] )
for ( P=D; P<M; P++ ) //插入排序
{
Tmp = E[P];
for ( i=P; i>=D && E[i-D].Cost >Tmp.Cost ; i-=D )
E[i] = E[i-D] ;
E[i] = Tmp;
}
}
int FindParent(int v) //查找集合
{
if(parent[v]==v) return v;
else return FindParent(parent[v]);
}
int Associate(int v,int w) //合并集合
{
int root1,root2;
root1=FindParent(v); //找到父亲
root2=FindParent(w); //找到父亲
if(root1!=root2) //如果父亲不一样
{
parent[root2]=root1; //合并两个集合
return 1;
}
return 0;
}
int Kruskal()
{
int i,GrossCost=0,Count=0;
Shell_Sort(); //希尔排序边集
for(i=1;i<=N;i++)parent[i]=i; //每个顶点一开始自己就是自己的父亲
for(i=0;i<M;i++)
{
if(Associate(FindParent(E[i].V ),FindParent(E[i].W )))
{
Count++; //边数+1
GrossCost+=E[i].Cost; //总价格更新
}
if(Count==N-1)break; //有N-1条边了结束
}
if(Count<N-1) return -1; //边数不够
else return GrossCost;
}
int main()
{
scanf("%d%d",&N,&M);
CreateGraph(N,M);
//printf("%d\n",Prime());
printf("%d\n",Kruskal());
return 0;
}