最小生成树

最小生成树

1)概念: 在一个无向带权图中,可以联通所有结点并且权值最小的路径就是最小生成树
2)实现算法:
2.1)Kruskal: 将所有边按照权值从大到小排序,然后利用并查集,判断每一条的边的的两个结点是否在同一集合中,不在就合并,并且加上该条边的权值,在就跳过,最后将所有边都处理完就可以得到最小的权值
2.2)Prim: 选一个起始结点,将该节点指向的结点和每条边的权值放入一个小根堆中(按权值建堆),每次去除堆顶的结点并判断该节点是否已被取出过,如果没有就加上这条边的权值,并将该结点的指向的结点加入堆中,不断重复上述步骤,直到堆为空
3)代码实现:

//Kruskal
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

//最小生成树 在无向带权图中,选择一些边,在保证连通性的情况下,总权值最小
// n 个 节点的最小生成树一定有 n-1 条边

//Kruskal算法:将边按权值排序,用并查集判断连接的边是否有环,
//无环就添加,有环就跳过

int n,m;
const int MAX = 100010;
int father[MAX];

vector<pair< pair<int,int>,int > >graph;

bool cmp(pair<pair<int,int>,int>&p1,pair<pair<int,int>,int>&p2){
    return p1.second < p2.second;
}

int find(int i){
    if(father[i] != i){
        father[i] = find(father[i]);
    }
    return father[i];
}

bool isSame(int a,int b){
    return find(a) == find(b);
}

void Union(int a,int b){
    int fa = find(a);
    int fb = find(b);
    if(fa != fb){
        father[fa] = fb;
    }
}


int main(){
    
    cin >> n >> m;

    for(int i = 1;i<=n;i++){
        father[i] = i;
    }

    for(int i = 0;i<m;i++){
        int u,v,w;
        cin >> u >> v >> w;
        graph.push_back({{u,v},w});
    }

    sort(graph.begin(),graph.end(),cmp);

    long long ans = 0;
    int cnt = 0;

    for(int i = 0;i<m;i++){
        int u = graph[i].first.first;
        int v = graph[i].first.second;
        int w = graph[i].second;
        //cout << w << endl;
        if(!isSame(u,v)){
            Union(u,v);
            ans += w;
            cnt++;
        }
    }

    if(cnt != n-1){
        cout << "orz" << endl;
        return 0;
    }

    cout << ans << endl;


    return 0;
}
//Prim
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
typedef long long ll;

int n,m;
const int MAX = 100000;


struct Cmp{
    bool operator()(const pair<int,int>&p1, const pair<int,int>&p2){
        return p1.second > p2.second;
    };
};


bool sset[MAX];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    priority_queue<pair<int,int>,vector<pair<int,int>>,Cmp>heap;

    

    cin >> n >> m;
    vector<vector<pair<int,int>>>graph(n+1);

    for(int i = 0;i<m;i++){
        int u,v,w;
        cin >> u >> v >> w;
        graph[u].push_back({v,w});
        graph[v].push_back({u,w});
    }

    sset[1] = true;

    for(const auto & edge : graph[1]){
       heap.push(edge);
    }
    int ans = 0;
    
    //集合中节点的数量
    int cnt = 1;
    while(!heap.empty()){
        int nxt = heap.top().first;
        int weight = heap.top().second;
        
        heap.pop();
        
        if(!sset[nxt]){
            sset[nxt] = true;
            ans += weight;
            cnt++;
            for(const auto &e : graph[nxt]){
                heap.push(e);
            }
        }
    }

    if(cnt != n){
        cout << "orz" <<endl;
        return 0;
    }
    
    cout << ans << endl;

    return 0;
}

Prim还有个优化版,但稍微有点复杂

//Prim improved
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl "\n";


const int MAXN = 50010;
const int MAXM = 500010;

//建图
vector<int>head(MAXN);
vector<int>nxt(MAXM);
vector<int>to(MAXM);
vector<int>weight(MAXM);
int cnt;

//建堆
vector<vector<int>>heap(MAXN,vector<int>(2));
int heapsize;
int nodeCnt;

//记录结点索引
vector<int>where(MAXN);


int n,m;

int u,w;

void swap(int i,int j){
    int a = heap[i][0];
    int b = heap[j][0];
    where[a] = j;
    where[b] = i;
    vector<int>temp = heap[i];
    heap[i] = heap[j];
    heap[j] = temp;
}

void heapInsert(int i){
    while(i!= 0 && heap[i][1] < heap[(i-1)/2][1]){
        swap(i,(i-1)/2);
        i = (i-1) / 2;
    }
}

void heapify(int i){
    int l = i * 2 + 1;
    while(l<heapsize){
        int best =  l+1 < heapsize && heap[l+1][1] < heap[l][1] ? l+1 : l; 
        best = heap[best][1] < heap[i][1] ? best : i;
        if(best == i){
            break;
        }
        swap(best,i);
        i = best;
        l = i * 2 + 1;
    }   
}

bool isEmpty(){
    return heapsize == 0;
}

void pop(){
    u = heap[0][0];
    w = heap[0][1];
    swap(0,--heapsize);
    heapify(0);
    where[u] = -2;
    nodeCnt++;
}

void addEdge(int u,int v,int w){
    nxt[cnt] = head[u];
    head[u] = cnt;
    to[cnt] = v;
    weight[cnt] = w;
    cnt++;
}

void addOrUpdateOrIngore(int ei){
    int v = to[ei];
    int w = weight[ei];
    if(where[v] == -1){
        heap[heapsize][0] = v;
        heap[heapsize][1] = w;
        where[v] = heapsize++;
        heapInsert(where[v]);
    }
    else if (where[v] >= 0){
        heap[where[v]][1] = min(heap[where[v]][1],w);
        heapInsert(where[v]);
    }
    // cout << "v: " << v << endl;
    // cout << "where[v] :" << where[v] << endl;
    // cout << "addORUp: prnit" << " " << heapsize << endl;
}

void build(){
    cnt = 1;
    heapsize = 0;
    nodeCnt = 0;
    for(int i = 0;i<=n;i++){
        where[i] = -1;
    }
    for(int i = 0;i<=n;i++){
        head[i] = 0;
    }
}

int prim(){
    nodeCnt = 1;
    where[1] = -2;
    for(int ei = head[1];ei > 0;ei = nxt[ei]){
        addOrUpdateOrIngore(ei);
    }
    int ans = 0;
    while(!isEmpty()){
        pop();
        ans += w;
        for(int ei = head[u];ei > 0;ei = nxt[ei]){
            addOrUpdateOrIngore(ei);
        }
    }

    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    
    cin >> n >> m;
    
    build();

    for(int i = 0;i<m;i++){
        int t1,t2,t3;
        cin >> t1 >> t2 >> t3;
        addEdge(t1,t2,t3);
        addEdge(t2,t1,t3);

    }

    

    int ans = prim();


    if(nodeCnt != n){
        cout << "orz" << endl;
    }
    else {
        cout << ans << endl;
    }
    // cout << nodeCnt << endl;
    // cout << ans << endl;




    return 0;
}

posted on 2026-05-05 21:17  Sean2299  阅读(10)  评论(0)    收藏  举报

导航