P1547 [USACO05MAR] Out of Hay S
解题思路与代码注释
题目理解
这道题目要求我们找到一个连通图的最小生成树中的最长边的长度。题目保证所有农场之间都是连通的,所以最小生成树一定存在。
解题方法
使用Prim算法来求解最小生成树,并在过程中记录下最长的边。Prim算法是一种贪心算法,从一个顶点开始,每次选择连接当前生成树和剩余顶点的最小权重边,直到所有顶点都包含在生成树中
#include<bits/stdc++.h> #define pii pair<int,int> // 定义pair类型别名,存储边的终点和权重 using namespace std; const int N = 1e5 + 10; // 定义最大农场数量 vector<pii> g[N]; // 邻接表存储图,g[x]存储与x相连的所有边 int n, m; // n-农场数量,m-道路数量 int dis[N], vis[N]; // dis数组记录各点到生成树的最小距离,vis标记是否已加入生成树 void prim() { // 使用小根堆优化,按距离排序 priority_queue<pii, vector<pii>, greater<pii>> q; memset(dis, 0x3f, sizeof(dis)); // 初始化距离为无穷大 dis[1] = 0; // 从1号农场开始 q.push({0, 1}); // 将起点加入优先队列 int ans = 0; // 记录最长边的长度 while(q.size()) { int x = q.top().second; // 当前距离生成树最近的顶点 int w = q.top().first; // 当前最小距离 q.pop(); if(vis[x]) continue; // 如果已经加入生成树则跳过 vis[x] = 1; // 标记为已加入 ans = max(ans, w); // 更新最长边(当前加入的边的权重) // 遍历所有邻接边 for(int i = 0; i < g[x].size(); i++) { int y = g[x][i].first; // 邻接顶点 int z = g[x][i].second; // 边权重 if(!vis[y] && dis[y] > z) { // 如果y未加入且找到更小的距离 dis[y] = z; // 更新距离 q.push({dis[y], y}); // 加入优先队列 } } } // 检查所有顶点的dis值,确保找到真正的最大值 // (因为优先队列可能包含过期的数据) for(int i = 1; i <= n; i++) ans = max(ans, dis[i]); cout << ans; // 输出最小生成树中的最长边 } int main() { cin >> n >> m; // 读入图数据 for(int i = 1; i <= m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); g[x].push_back({y, z}); // 无向图,双向添加 g[y].push_back({x, z}); } prim(); // 执行Prim算法 return 0; }
关键点解释
-
Prim算法核心:
-
使用优先队列(小根堆)来高效获取当前距离生成树最近的顶点
-
dis数组记录各顶点到生成树的最小距离 -
vis数组标记顶点是否已加入生成树
-
-
最长边记录:
-
每次从队列中取出顶点时,用该边的权重更新最大值
-
最后还需要检查所有
dis值,因为优先队列中可能存在过期的数据
-
-
算法复杂度:
-
时间复杂度:O(M log N),其中M是边数,N是顶点数
-
空间复杂度:O(N + M)
-
kruskal解法:来自CJ的远古无注释版本
#include<bits/stdc++.h> using namespace std; const int N = 10001; struct node{ int x,y,z;//创建边的结构体,x,y,z分别代表x到y两点的权值为z }a[N]; int f[N]; bool cmp(node a,node b) { return a.z<b.z; } int find(int x) { if(f[x]!=x)f[x] = find(f[x]); return f[x]; } int main() { int n,k;cin>>n>>k; int maxsum = 0; for(int i=1;i<=k;i++) cin>>a[i].x>>a[i].y>>a[i].z,maxsum+=a[i].z; sort(a+1,a+1+k,cmp);//排序,按权值z从小到大 int sum = 0,ans = 0;//当前已连接结点sum=0,最小花费ans=0 for(int i=1;i<=n;i++)f[i] = i; int maxx = -1; for(int i=1;i<=k;i++) { int fx = find(a[i].x); int fy = find(a[i].y); if(fx!=fy)//证明x和y是可以构成连接的 { f[fy] = fx;//让fy父节点设为fx sum++;//已连接结点数sum++ ans+=a[i].z;//最小花费ans则是第i条边的权值 maxx = max(maxx,a[i].z); if(sum==n)break;//当已连接数sum==n时,最小生成树完成 } } cout<<maxx; return 0; }

浙公网安备 33010602011771号