一,定义:
将无向连通图连起来的生成树中需要的权值最小的生成树为最小生成树
主要有两种算法
prim算法(暴力O(n^2+m)还有kruskal算法(mlogm)。
P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
以下代码都是写这模板题的
prim算法
思路:贪心(最短路)
prim写法几乎与dijkstra没什么区别。思想就是首先以任意点为起点,然后遍历与他有连接的点,每次选权值最小的边对应的点(同时也更新其他点到起点这个集合(不是到起点)的最短距离即可)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define int long long
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f; //int型的INF
const int N = 2e5 + 10;
struct node
{
int next,to;
int w;
} edge[N<<1];
int head[N],num;
int dis[N];
bool vis[N];
void add(int u,int v,int w)
{
edge[++num].next=head[u];
edge[num].to=v;
edge[num].w=w;
head[u]=num;
}
int prim(int n)
{
int ans=0;
int cnt=0;
fill(dis,dis+n+1,INF);//初始赋值所有点到起点集合距离无穷大
dis[1]=0;
priority_queue<pii,vector<pii>,greater<pii>>q;//堆优化
q.push({dis[1],1});
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(vis[u])continue;//对于u,取他离集合最短时的边
vis[u]=1;
cnt++;//记录存入集合的点+1
ans+=dis[u];//一旦取了,加入这个边
for(int i=head[u]; i; i=edge[i].next)
{
int v=edge[i].to,w=edge[i].w;//更新u连接的其他点v,更新v点们到集合的最短距离
if(dis[v]>w)//区别dij,这里更新其到集合(不是到起点)最短边即可
{
dis[v]=w;
q.push({dis[v],v});
}
}
}
return (cnt==n?ans:-1);//如果存入了图的所有点,说明连通
}
void mysolve()
{
int n,m;
cin>>n>>m;
int x,y,w;
while(m--)cin>>x>>y>>w,add(x,y,w),add(y,x,w);
int ans=prim(n);
if(ans==-1)cout<<"orz"<<endl;
else cout<<ans<<endl;
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t=1;
//cin >> t;
while (t--)
{
mysolve();
}
system("pause");
return 0;
}
kruskal算法
思路:并查集
克鲁斯卡尔思路是一开始把n个点看成n个独立的集合,每次寻找图中的最短边(没有使用过或者舍弃过),然后如果这条边可以使图中的集合-1,那么取,否则舍去。
所以预处理就是把图中的边排序
如果最后图中只剩一个集合,成功获得最小生成树
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define int long long
const int N = 2e5 + 10;
struct node
{
int u,v,w;
bool operator<(const node&k)
{
return w<k.w;
}
} edge[N<<1];
int fa[N];
int find(int x)
{
if(fa[x]!=x)fa[x]=find(fa[x]);
return fa[x];
}
int kruskal(int n,int m)
{
int cnt=n,ans=0;//cnt记录图中集合个数
sort(edge+1,edge+1+m);//排序边
for(int i=1; i<=m; ++i)
{
int u=edge[i].u,v=edge[i].v,w=edge[i].w;
int fu=find(u),fv=find(v);
if(fu==fv)continue;//如果属于一个集合,该边舍去
ans+=w;
fa[fv]=fu;//合并集合
cnt--;
}
return (cnt==1?ans:-1);//集合为一说明图连通
}
void mysolve()
{
int n,m;
cin>>n>>m;
for(int i=1; i<=n; ++i)fa[i]=i;
for(int i=1; i<=m; ++i)cin>>edge[i].u>>edge[i].v>>edge[i].w;
int ans=kruskal(n,m);
if(ans==-1)cout<<"orz"<<endl;
else cout<<ans<<endl;
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t=1;
//cin >> t;
while (t--)
{
mysolve();
}
system("pause");
return 0;
}