划重点:必考题!!
Description
给定结点数为n,边数为m的带权无向连通图G,所有结点编号为1,2,3....n。 求图G的最小生成树的边权和。输入格式
第一行两个正整数n和m。n,m<=2000 之后的m行,每行三个正整数a,b,w,描述一条连接结点a和b,边权为w的边。1=<a,b<=n,w<=10^18。 注意可能存在重边和自环。输出格式
一个整数表示图G的最小生成树的边权和(注意用长整型)。输入样例
7 12 1 2 9 1 5 2 1 6 3 2 3 5 2 6 7 3 4 6 3 7 3 4 5 6 4 7 2 5 6 3 5 7 6 6 7 1输出样例
16
卡了好多天的题,翻阅学习了大量其他大佬们的代码和思路
自己试着理解了一下,都在注释里(很可能有悟不到点的地方👉👈)
用到的是Prim算法求最小生成树
在包含n个顶点的联通图中,找出只有(n-1)条边包含所有n个顶点的连通子图。
代码如下:(Prim算法)
#include <iostream>
#include <vector>
#include <cstring>
#include <limits.h>
using namespace std;
long long n,m,i,j,k,x,y,z;
struct node
{
long long adj;//顶点
long long value;//边上的权值
};
vector<node> e[100000];
long long d[100000],v[100000];
/**< 返回相邻边最小的权值 */
long long getmin()
{
long long minv=LLONG_MAX,mini,i;//i可不能省
for(i=1;i<=n;i++)//题设:所有结点编号为1,2,3...n
{
if(v[i]==0&&d[i]<minv)
{
minv=d[i];//找到了最小权值
mini=i;//最小边的一个顶点
}
}
return mini;
}
void prim()
{
memset(d,127,sizeof(d));//初始化最大值
d[1]=0;
long long sum=0;
for(i=0;i<n;i++)//n次循环结束,则所有的顶点也遍历到了
{
long long t=getmin();
v[t]=1;//标记
sum+=d[t];//记录相邻边的最小权值
for(j=0;j<e[t].size();j++)//相邻顶点
{
long long adj=e[t][j].adj,value=e[t][j].value;
if(value<d[adj])
d[adj]=value;//相当于把相邻顶点权值放进d数组中
}
}
cout<<sum;
}
int main()
{
ios::sync_with_stdio(false);
memset(v,0,sizeof(v));//对比较大的数组进行初始化,此时初始化标记数组
cin>>n>>m;
for(i=0;i<m;i++)
{
cin>>x>>y>>z;
e[x].push_back({y,z});
e[y].push_back({x,z});//无向
}
prim();
return 0;
}