赞
踩
在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此的边权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树(Minimum Spanning Tree(MST) )。最小生成树其实是最小权重生成树的简称。
简单来说,就是给你很多个点,然后求将这些点连接起来的所有边的最小长度和。
待我们先看一道题理解一下在什么情况下用prim算法求最小生成树
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。
接下来M 行每行包含三个整数Xi,Yi,Zi,表示有一条长度为 Zi 的无向边连接结点 Xi,Yi。
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
输入 #1
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1
7
我们现在有一个无向图(就是一张图),这时候我们要从点1开始然后将所有点连接起来,计算边的长度。现在我们构建两个集合S(红色的点),V(蓝色的点),S集合中存放的是已近加入最小生成树的点,V集合中存放的是还没有加入最小生成树的点。显然刚开始时所有的点都在V集合中。
1.我们开始时初始化其他点到点1的距离即到集合S的距离都为正无穷
2.我们将ans作为所求值,刚开始应该记为零。每一次我们选择离集合里面点有最近距离的点加入集合中同时更新dist数组,以及ans的值
3.在目前情况下,S集合中只有点1,这时距离S集合最近的点应该为点2,我们将点2加入集合S中,同时将dist数组和ans的值进行更新。
4.加入点2后,现在集合中有的边为23,即ans更新为23,同时对于没有在集合中的点到集合的距离进行更新,如点7应该由36更新为1。
5.然后重复上面所有步骤,直到我们的集合中包含了所有的点......
6.直到最后,我们可以发现这些点如果能被连接起来,那么需要的最小的边的长度应该为57,即权值和为57。
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。
接下来M 行每行包含三个整数Xi,Yi,Zi,表示有一条长度为 Zi 的无向边连接结点 Xi,Yi。
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
输入 #1
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1
7
这里我们说两种代码,第一种可以理解一下(方法没错,这道题ac不了的原因是他给的范围很大是第一种方法的二维数组爆了),第二种就是是为了防止有些恶心题目开的范围太大使用的优化算法(这个可以ac哦)(第一种改了一个晚上才弄出来,第二种又爆肝了一晚上)。不说了,步入正题。
1.正常(符合上面图解)的prim代码
- #include <bits/stdc++.h>
- using namespace std;
- const int N = 10001;
- const int INF = 998244353;
- int g[N][N], dist[N], n, m, res;
- bool vis[N];
- void prim()
- {
- dist[1] = 0;//把点1加入集合S,点1在集合S中,将它到集合的距离初始化为0
- vis[1] = true;//表示点1已经加入到了S集合中
- for (int i = 2; i <= n; i++)
- {
- dist[i] = min(dist[i], g[1][i]);
- }//用点1来更新dist数组
- for (int i = 2; i <= n; i++) //计数器,表示选了几次了,正在选第几个点
- {
- int temp = INF;//初始化距离为无穷大
- int t = -1;//接下来去寻找离集合S最近的点加入到集合中,用t记录这个点的下标
- for (int j = 2; j <= n; j++) //判断正在几号点
- {
- //如果这个点没有加入集合S,而且这个点到集合的距离小于temp就将下标赋给t
- if (vis[j] == false && dist[j] < temp)
- {
- temp = dist[j];//更新集合V到集合S的最小值
- t = j;//把点赋给t
- }
- }
- if (t == -1)//表明没有适合的点,将res赋值正无穷表示构建失败,结束函数
- {
- res = INF;
- return;
- }
- vis[t] = true;//表明这个点找到过了,不用再使用这个点了
- res += dist[t]; //注意是dist[t]而不是dist[i],这里的i只起到计次数作用
- for (int j = 2; j <= n; j++)
- {
- dist[j] = min(dist[j], g[t][j]);//取较小的dist[i]
- }
- }
- }
- int main()
- {
- cin >> n >> m;
- for (int i = 1; i <= n; i++)
- {
- for (int j = 1; j <= n; j++)
- {
- g[i][j] = INF; //设刚开始所有点之间的距离为无穷大
- }
- dist[i] = INF; //点到集合上任意一个点都为无穷大
- }
- for (int i = 1; i <= m; i++)
- {
- int u, v, w;
- cin >> u >> v >> w;
- g[u][v] = g[v][u] = min(w,g[u][v]); // u与v之间的距离为w
- }
- prim();
- if (res == INF)
- {
- cout << "orz" << endl;//如果还是无穷大则输出
- }
- else
- {
- cout << res << endl;//不是无穷大则输出正常值
- }
- system("pause");
- return 0;
- }

2.优化prim算法(队列)
- #include <bits/stdc++.h>
- using namespace std;
- const int N=20001;
- int dist[N];//到集合的距离
- bool vis[N];//标记是否存入集合中
- int n,m,ans;
- typedef struct node{
- int pos;
- int dis;
- bool operator<(const node &x) const
- {
- return x.dis<dis;
- //从小到大排序
- }
- }node;
- typedef struct edge{
- int v;
- int w;
- }edge;
- vector<edge> connects[N];//点之间的距离
- const int INF=998244353;
- void init(){
- for(int i=1;i<=n;i++){
- dist[i]=INF;//刚开始距离全部初始化为无穷大
- vis[i]=false;//表示都还没有访问
- }
- }
- void prim(){
- dist[1]=0;//1的距离为零
- vis[1]=true;//表示1访问过了,已存入集合中
- priority_queue<node> q;//构建优先队列,优先计算有多条边的两个点的较小的边的值
- q.push({1,0});//将点1存入
- while(!q.empty()){
- int pos=q.top().pos;
- int dis=q.top().dis;
- q.pop();
- vis[pos]=true;//存入就标记
- for(int i=0;i<connects[pos].size();i++){//将一个点周围所有点进行搜索
- int v=connects[pos][i].v;
- int w=connects[pos][i].w;
- if(vis[v]==1){//表示已经存入了,不用再进行下面操作了
- continue;
- }
- if(dist[v]>w){//如果距离小于dist数组距离就更新
- dist[v]=w;
- q.push({v,dist[v]});
- }
- }
- }
- for(int i=1;i<=n;i++){
- ans+=dist[i];//最后将所有最小值加起来
- }
- if(ans>=INF){//如果依然是无穷大,表示为构建成功最小生成树,输出orz
- cout<<"orz"<<endl;
- }else{
- cout<<ans<<endl;//不是无穷大则正常输出
- }
- }
- int main(){
- cin>>n>>m;
- init();
- for(int i=1;i<=m;i++){
- int u,v,w;
- cin>>u>>v>>w;//开始存入点之间的距离
- connects[u].push_back({v,w});
- connects[v].push_back({u,w});
- }
- prim();
- system("pause");
- return 0;
- }

最后,完结撒花!
不要忘了轻轻关注一下这位为了这个算法爆肝了两个晚上的某帅气(趁现在还没怎么掉头发)博主!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。