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;
}

关键点解释

  1. Prim算法核心

    • 使用优先队列(小根堆)来高效获取当前距离生成树最近的顶点

    • dis数组记录各顶点到生成树的最小距离

    • vis数组标记顶点是否已加入生成树

  2. 最长边记录

    • 每次从队列中取出顶点时,用该边的权重更新最大值

    • 最后还需要检查所有dis值,因为优先队列中可能存在过期的数据

  3. 算法复杂度

    • 时间复杂度: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;
}

 

posted @ 2025-05-21 16:12  CRt0729  阅读(14)  评论(0)    收藏  举报