SPOJ:368 Cobbled streets

经典题目,就是让你求最小生成树。

这里我用的是克鲁斯卡尔算法。

kruskal算法所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。kruskal算法分e 步,其中e 是网络中边的数目。按耗费递增的顺序来考虑这e 条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。

 

用并查集处理是否出现环。

注意father[find(a)]=find(b);这里是两个树根合并,一开始没注意WA了一次。

当生成树边数到达n-1时说明已经成为最小生成树,可以直接跳出。这样用时会少一点。

 

 

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct Edge
{
    int u,v,cost;
};
Edge a[300005];
int father[1005]={0};
int n,m;
bool cmp(Edge a,Edge b)
{
    return a.cost<b.cost;
}
void init_union_find()
{
    for(int i=1;i<=n;++i)
    father[i]=i;
}
int find(int p)
{
    return father[p]==p?p:find(father[p]);
}
void unite(int a,int b)
{
    father[find(a)]=find(b);
}
void Kruskal(int &ans)
{
   sort(a,a+m,cmp);
   init_union_find();
   int count=0;
   for(int i=0;i<m;++i)
   {
       Edge t=a[i];
       if(find(t.u)!=find(t.v))
       {
           unite(t.u,t.v);
           ans+=t.cost;
           count++;
           if(count==n-1) break;
       }
   }
}
int main()
{
   int T;
   scanf("%d",&T);
   while(T--)
   {
   int p,ans=0;
   scanf("%d%d%d",&p,&n,&m);
   for(int i=0;i<m;++i)
    scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].cost);
   Kruskal(ans);
   printf("%d\n",p*ans);
   }
   return 0;
}



你可能感兴趣的:(最小生成树,kruskal,并查集,贪心法)