题目
http://acm.hdu.edu.cn/showproblem.php?pid=1863
问题分析
这道题目求使全省畅通的最低成本,把村庄作为顶点,把道路作为边,那么该题即求最小生成树的总边权值。
算法
算法核心
其实我觉得kruskal算法也是一种贪心,就是每次都选择权重最小的边(也就是成本最低的道路),看这条边的两个顶点是否处在同一个连通分支中,如果不在同一个连通分支中,那么就选上这条边,直至整个图连通,也就是形成最小生成树。
数据结构
本题采用并查集,这种结构的两个主要方法就是查找和合并,查找即查找当前结点的根结点,合并就是将两个连通分支合为一个。为了避免出现线性结构而造成不必要的开支,可以进行优化,即在查找时如果x的根结点不是x,就让root[x]=find(root[x]),这个语句可以理解为将x结点直接连到根结点上。合并时,也是将这两个分支的根结点合并。
算法流程
首先将所有结点的根结点初始化为本身,因为一开始并没有路,然后将边按照权重从小到大排序,之后进行遍历,如果当前边的两个顶点的不在同一个连通分支内,即两个顶点的根结点不同,则选择这条边,边数增加,成本增加,将两个顶点所在的连通分支合并,即根据根结点的大小关系决定把谁的根结点连到另一个的根结点上(直接赋值即可),循环直至边数等于顶点数-1(树的性质)。
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=105;
struct road
{
int b;//起点
int e;//终点
int v;//成本
} a[maxn*maxn/2];
int root[maxn];
int n,m;//道路数,村庄数
int ans;//最低成本
int cnt;//要修建的道路数目
void init()
{
for(int i=0; i<maxn; ++i)
root[i]=i;
ans=0;
cnt=0;
}
bool sort1(road r1,road r2)//按照成本从低到高排序
{
return r1.v<r2.v;
}
int find_(int x)
{
if(x==root[x])
return x;
else
{
root[x]=find_(root[x]);
return root[x];
}
}
void kruskal()
{
init();
sort(a+1,a+n+1,sort1);
for(int i=1; i<=n; ++i)
{
int fb,fe;//记录两顶点的根结点
fb=find_(a[i].b);
fe=find_(a[i].e);
if(fb!=fe)
{
if(fb<fe)
root[fe]=fb;
else
root[fb]=fe;
ans+=a[i].v;
cnt++;
if(cnt==m-1)
break;
}
}
}
int main()
{
while(cin>>n>>m&&n)
{
int i;
for(i=1; i<=n; ++i)
cin>>a[i].b>>a[i].e>>a[i].v;
kruskal();
if(cnt==m-1)
cout<<ans<<endl;
else
cout<<"?"<<endl;
}
return 0;
}