3 条题解

  • 0
    @ 2026-6-20 15:32:45

    // Kruskal算法 O(mlogm)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=200010;
    int n,m;
    int fa[N],ans,tot;
    pair<int,pair<int,int> >e[N]; //边集
    
    int find(int u){ //并查集的找根
      return fa[u]==u?u:fa[u]=find(fa[u]);
    }
    void kruskal(){
      for(int i=1;i<=n;i++) fa[i]=i;
      sort(e+1,e+1+m); //排序
      for(int i=1;i<=m;i++){
        int x=find(e[i].second.first),y=find(e[i].second.second);
        if(x!=y){
          fa[x]=y;
          ans+=e[i].first;
          if(++tot==n-1) break;
        }
      }
      if(tot==n-1) printf("%d\n",ans);
      else puts("orz");
    }
    int main(){
      cin>>n>>m;
      for(int i=1,u,v,w;i<=m;i++){
        cin>>u>>v>>w;
        e[i]={w,{u,v}};
      }
      kruskal();
    }
    
    • 0
      @ 2026-6-20 15:28:44

      // Prim算法 O(n^2)
      #include<bits/stdc++.h>
      using namespace std;
      
      const int N=5010;
      int n,m,cnt,ans;
      vector<pair<int,int>> e[N];
      int d[N],vis[N]; //d[i]表示点i到集合内点的最小边权
      
      void prim(){
        for(int i=0;i<=n;i++) d[i]=1e9; d[1]=0;
        for(int i=1,u;i<=n;i++){
          u=0; //0点很大
          for(int j=1;j<=n;j++)if(!vis[j]&&d[j]<d[u]) u=j;
          vis[u]=1;    //选出最优点
          if(u) cnt++; //点数
          ans+=d[u];   //边权和
          for(auto [v,w]:e[u])if(d[v]>w) d[v]=w;
        }
        
        if(cnt==n) printf("%d\n",ans);
        else puts("orz");
      }
      int main(){
        cin>>n>>m;
        for(int i=0,a,b,c; i<m; i++){
          cin>>a>>b>>c;
          e[a].push_back({b,c});
          e[b].push_back({a,c});
        }
        
        prim();
      }
      
      // 堆优化的 Prim 算法 O(mlogm)
      #include<bits/stdc++.h>
      using namespace std;
      
      #define inf 1e9
      const int N=5010;
      int n,m,ans,cnt;
      vector<pair<int,int>> e[N];
      int d[N],vis[N]; //d[v]表示v到已选集合的最短距离
      
      bool prim(int s){
        for(int i=0;i<=n;i++) d[i]=inf; d[s]=0;
        priority_queue<pair<int,int>> q; //大根
        q.push({0,s}); //起点入队
        while(q.size()){
          int u=q.top().second; q.pop();
          if(vis[u]) continue; //第1次出队才扩展
          vis[u]=1;
          ans+=d[u]; //边权和
          cnt++;     //节点个数
          for(auto t:e[u]){
            int v=t.first, w=t.second;
            if(d[v]>w){ //松弛
              d[v]=w;
              q.push({-d[v],v}); //入队
            }
          }
        }
        return cnt==n;
      }
      int main(){
        cin>>n>>m;
        for(int i=1,a,b,c; i<=m; i++){
          cin>>a>>b>>c;
          e[a].push_back({b,c});
          e[b].push_back({a,c});
        }
        if(!prim(1)) puts("orz");
        else printf("%d\n",ans); 
      }
      
      • 0
        @ 2026-3-23 11:57:47

        【算法速成课1 | 题解】洛谷P3366 【模板】最小生成树 MST(Prim & Kruskal)-CSDN博客

        D07 最小生成树 Prim 算法

        D08【模板】最小生成树 Kruskal 算法

        /*
        Kruskal:
        Kruskal 算法也是一种贪心算法,但它是基于边的选择:
        
        将所有边按权重从小到大排序。
        依次选择最小权重的边。
        如果该边连接的两个顶点不在同一连通分量中(不形成环),则加入生成树。
        重复步骤 2 和 3,直到选够 N - 1 条边(N 是节点个数)。
        
        */
        
        
        #include<bits/stdc++.h>
        using namespace std;
         
        const int N = 5e5 + 10;
        typedef long long LL;
         
        int fa[N];
        struct node {
        	int x, y;
        	LL v;
        } a[N];
         
        bool cmp(node na, node nb) {
        	return na.v < nb.v;
        }
         
        int findfa(int x) {
        	if (fa[x] == x) {
        		return fa[x];
        	}
        	return fa[x] = findfa(fa[x]);
        }
         
        int main() {
        	ios::sync_with_stdio(false);
        	cin.tie(0);
        	
        	int n, m;
        	cin >> n >> m;
        	
        	for (int i = 1; i <= m; i++) {
        		cin >> a[i].x >> a[i].y >> a[i].v;
        	}
        	
        	for (int i = 1; i <= n; i++) {
        		fa[i] = i;
        	}
        		
        	LL ans = 0;  //累计答案 
        	int sum = 0;  //累计已选的边数 
        	sort(a + 1, a + m + 1, cmp);
        	
        	for (int i = 1; i <= m; i++) {
        		int tx = findfa(a[i].x);
        		int ty = findfa(a[i].y);
        		
        		if (tx != ty) {
        			sum ++;
        			ans += a[i].v;
        			fa[tx] = ty;
        		}
        		
        		if (sum == n - 1) {  //找够边就退出 
        			break;
        		}
        	}
        	
        	if (sum < n - 1) {   //都结束了还选不到 n - 1 条合适的边,包不连通的 
        		cout << "orz" << "\n";
        		return 0;
        	}
        	
        	cout << ans << "\n";
        	
        	return 0;
        }
        
        /*Prim 算法是一种贪心算法,从一个起始顶点开始,逐步扩展生成树:
        
        从任意顶点开始,将其加入生成树集合。
        重复选择与当前生成树相连的最小权重边。
        将新顶点加入生成树,直到覆盖所有顶点。
        */
        #include<bits/stdc++.h>
        using namespace std;
         
        typedef long long LL;
        const int N = 5e5 + 10;
         
        struct node{
        	int x; LL v;
        } ;
        vector<node> G[N];
        bool v[N];   //v[i]:为 ture就是 i走过,反之则没有 
        int n, m; 
         
        bool operator<(node na, node nb) {
        	return na.v > nb.v;
        }
         
        void Prim(){
        	LL ans = 0;
        	
        	priority_queue<node> Q;
        	memset(v, 0, sizeof(v));
        	int st = 1, sum = 1;   // st:起始点,sum:走过多少点 
        	v[st] = 1;
        	
        	for(auto i: G[st]) {
        		Q.push(i);
        	}
        	
        	int Time = 0;   //特判是否是连通图用的循环次数计数 
        	while (sum < n) {
        		if (Time > 3 * m) {  //每条边最多走两遍,超过三遍那肯定不连通 
        			cout << "orz" << "\n";
        			return ;
        		}
        		Time++;   //不能放下面那个 if(v)下面!!! 
        		
        		auto Head = Q.top();
        		Q.pop();
        		
        		int x = Head.x;
        		if (v[x]) {   //虽然下面进堆之前就特判了
        		//但是历史遗留的边的点可能已经走过了 
        			continue;
        		}
        		v[x] = 1;
        		sum++;
        		ans += Head.v;
        		
        		for (auto j: G[x]) if(!v[j.x]) {
        			Q.push(j);
        		}
        	}
        	
        	cout << ans << "\n";
        }
         
        int main() {
        	ios::sync_with_stdio(false);
        	cin.tie(0);
        	
        	cin >> n >> m;
        	for (int i = 1; i <= m; i++) {
        		int x, y; LL v;
        		cin >> x >> y >> v;
        		G[x].push_back({y, v});
        		G[y].push_back({x, v});
        	}
        	Prim();
        	
        	return 0;
        } 
        
        
        • 1

        信息

        ID
        263
        时间
        1000ms
        内存
        256MiB
        难度
        7
        标签
        递交数
        460
        已通过
        90
        上传者