算法过程:(贪心,动态规划)
将图中点分成两个集合,一个是在最小生成树中,一个尚不在。称为P,Q集合(用book数组标记)
dis[i]代表某个阶段第i个点到最小生成树的最近的距离(实质是边的权值).最开始都为INF.
最开始P集合为空,Q为全集
最终态P为全集,Q集合为空
1.将任意一个点放入优先队列W.对应dis值置0.
2.当W不为空时,从里面拿出一个离最小生成树最近的一个边.(最开始为0)
3.对这个边的终点E进行拓展,将所有能够更新dis数组的边推入W(边的终点S要在Q集合中).
4.跳到2.
解释:
复杂度:O(MlogM) ----M为边数
①过程大致是从邻接着现阶段最小生成树的所有边里选取最短的一个边去拓展这颗树.
②走完之后得到的dis数组就是最小生成树的边.(如果想输出路径,另开数组保存信息就好)
③Djistra求单源最短路把dis数组的含义从 从i到最小生成树集合最小距离改成i到起点s的最小距离就好.
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxe = 4e5+5; //无向边记得开两倍大!
const int maxv = 5e3+5;
const int INF = 0x3f;
int u[maxe],v[maxe],w[maxe],first[maxv],nextt[maxe];
int sign,n,m,vis[maxv];
struct Node
{
int num,dis;
Node(int n = 0,int d = 0)
{
num = n,dis = d;
}
bool operator < (const Node & a) const
{
return dis > a.dis; //dis小的排前面
}
}node[maxe];
void addedge(int x,int y,int z)
{
u[++sign] = x;
v[sign] = y;
w[sign] = z;
nextt[sign] = first[x];
first[x] = sign;
}
priority_queue<Node>Q;
int prim(int s)
{
int ans = 0;
int dis[maxv];
memset(dis,INF,sizeof dis);
dis[s] = 0;
Node temp(s,0);
Q.push(temp); //把起点推入队列
while(!Q.empty())
{
temp = Q.top();
Q.pop();
int g = temp.num;
if(vis[g]) continue; //已经在P集合的节点不再更新
vis[g] = 1;
for(int i = first[g];i;i = nextt[i])
{
if(dis[v[i]] > w[i] && !vis[v[i]])
{
dis[v[i]] = w[i];
Q.push(Node(v[i],dis[v[i]]));
}
}
}
for(int i = 1;i<=n;i++)
if(!vis[i])
return -1;
else
ans += dis[i];
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
int x,y,z;
for(int i = 1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
addedge(y,x,z);
}
int ans = prim(x);
if(ans == - 1)
printf("orz\n");
else
printf("%d\n",ans);
return 0;
}
//
/*
4 3
1 2 7
1 3 4
2 3 1
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
*/