#include<bits/stdc++.h>
using namespace std;
#define ios \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
struct road
{
int t,l,next;
}edge[2*500000+100];
int head[100010];
int vis[100010];
int cnt=0;
void addedge(int a,int b,int v)
{
edge[++cnt].t=b;
edge[cnt].l=v;
edge[cnt].next=head[a];
head[a]=cnt;
}
struct ty2
{
int x,dis;
bool operator < (const ty2 &a)const
{
return dis>a.dis;
}
};
priority_queue<ty2>q;
void prim()
{
int ans=0;
vis[1]=1;
ty2 tmp;
for(int i=head[1];i!=-1;i=edge[i].next)
{
tmp.x=edge[i].t;
tmp.dis=edge[i].l;
q.push(tmp);
}
while(!q.empty())
{
ty2 tmp=q.top();
q.pop();
int x=tmp.x;
if(vis[x]) continue;
ans+=tmp.dis;
vis[x]=1;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(vis[edge[i].t]) continue;
tmp.x=edge[i].t;
tmp.dis=edge[i].l;
q.push(tmp);
}
}
cout<<ans<<endl;
}
int main()
{
ios;
int n,m;
cin>>n>>m;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int a,b,v;
cin>>a>>b>>v;
addedge(a,b,v);
addedge(b,a,v);
}
prim();
}
用len数组优化prim 牛客算法入门班第三节课
len数组的含义就是该点到这个已经生成的树的距离
其主要思想就是如果该点到已经生成的树的距离已经确定且小于从树上新拓展的边
则不需要进行入队操作
#include<bits/stdc++.h>
using namespace std;
#define ios \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
struct road
{
int t,l,next;
}edge[2*500000+100];
int head[100010];
int vis[100010];
int cnt=0;
void addedge(int a,int b,int v)
{
edge[++cnt].t=b;
edge[cnt].l=v;
edge[cnt].next=head[a];
head[a]=cnt;
}
struct ty2
{
int x,dis;
bool operator < (const ty2 &a)const
{
return dis>a.dis;
}
};
int len[100010];
priority_queue<ty2>q;
void prim()
{
int ans=0;
vis[1]=1;
ty2 tmp;
memset(len,0x7f,sizeof(len));
for(int i=head[1];i!=-1;i=edge[i].next)
{
tmp.x=edge[i].t;
tmp.dis=edge[i].l;
len[tmp.x]=tmp.dis;
q.push(tmp);
}
while(!q.empty())
{
ty2 tmp=q.top();
q.pop();
int x=tmp.x;
if(vis[x]) continue;
ans+=tmp.dis;
vis[x]=1;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(vis[edge[i].t]) continue;
if(len[edge[i].t]>edge[i].l)
{
tmp.x=edge[i].t;
len[edge[i].t]=tmp.dis=edge[i].l;
q.push(tmp);
}
}
}
cout<<ans<<endl;
}
int main()
{
ios;
int n,m;
cin>>n>>m;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int a,b,v;
cin>>a>>b>>v;
addedge(a,b,v);
addedge(b,a,v);
}
prim();
}
prim应用的也是贪心每次找出到树上长度最小的边如果呢个边的点在树上则跳过否则加进来
与dijkstra的区别就是在贪心的时候贪心的东西不一样
Kruskal算法贪心把边从小到大进行排序
然后按边从小到大顺序看连接该边的两个点是否在同一个集合里面
在的话就直接进行连接
#include<bits/stdc++.h>
using namespace std;
int fa[100010];
int find(int x)
{
return fa[x]=fa[x]==x?x:find(fa[x]);
}
struct road
{
int x,y,z;
}edge[500010];
bool cmp(road A,road B)
{
return A.z<B.z;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>edge[i].x>>edge[i].y>>edge[i].z;
}
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
sort(edge+1,edge+1+m,cmp);
int ans=0;int cnt=0;
for(int i=1;i<=m;i++)
{
int fx=find(edge[i].x);
int fy=find(edge[i].y);
if(fx==fy) continue;
ans+=edge[i].z;
fa[fx]=fy;
cnt++;
if(cnt>=n-1) break;
}
cout<<ans;
}
prim和kruskal的贪心策略是一样的都是选取耗费最小的边
对于prim,其选取的边(u,v)必有一个顶点已经被覆盖,另一个顶点未被覆盖
而对于kruskal,其选取的边(u,v)任意,只要这个边的加入不能使被覆盖的点构成回路
prim算法的时间复杂度为O((n+m)logm)-适用于稠密图
kruskal算法的时间复杂度为O(mlogm)-适用于稀疏图
现在的时间差距并不明显