把所有边从小到大排序,能加就加,用并查集维护联通性,如果一个边两边的点已经在同一个连通块里了就不要这条边了
时间复杂度O(mlogm)
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long #define maxn 200010 using namespace std; ll n,m,sum=0,cnt=0; ll fa[maxn]; struct bcj{ void init(ll n){ for(int i=1;i<=n;i++) fa[i]=i; } ll f(ll x){ return fa[x]=(fa[x]==x)?x:f(fa[x]); } bool u(ll x,ll y){ x=f(x),y=f(y); if(x==y) return false; fa[x]=y; return true; } }s; struct edge{ ll u,v,w; bool friend operator < (edge a,edge b){ return a.w<b.w; } }e[maxn]; int main(){ cin>>n>>m; s.init(n); for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w; sort(e+1,e+m+1); for(int i=1;i<=m;i++) if(s.u(e[i].u,e[i].v)){ sum+=e[i].w; cnt++; } if(cnt!=n-1){ cout<<"orz"<<endl; return 0; } cout<<sum<<endl; return 0; }
随便找一个点当树根,接下来每次加入一个新点,选择离当前生成树最近的点加入就可以了
时间复杂度O(n2),可用二叉堆优化到O(mlogn),主要用于稠密图尤其是完全图的最小生成树的求解
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #define ll long long #define maxn 200010 using namespace std; template<typename T> inline void read(T &x){ x=0; bool flag=0; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) flag=1; for(;isdigit(c);c=getchar()) x=x*10+(c^48); if(flag) x=-x; } struct data{ ll dis;ll id; bool friend operator < (data a,data b){ return a.dis>b.dis; } }; priority_queue <data> pq; ll v[maxn<<1],x[maxn<<1],w[maxn<<1],al[maxn],ct;//maxn<<1相当于maxn*2 bool visit[maxn]; void add(ll U,ll V,ll va){ v[++ct]=V; x[ct]=al[U]; al[U]=ct; w[ct]=va; } int main(){ ll n,m,u1,v1,w1,ans=0,cnt=0; read(n);read(m); for(int i=1;i<=m;i++){ read(u1);read(v1);read(w1); add(u1,v1,w1);add(v1,u1,w1);// } pq.push((data){0,1}); while(!pq.empty()){ ll u=pq.top().id; ans+=pq.top().dis; cnt++; visit[u]=true; pq.top(); for(ll i=al[u];i;i=x[i]){ if(!visit[v[i]]) pq.push((data){w[i],v[i]}); } while(!pq.empty()&&visit[pq.top().id]) pq.pop(); } if(cnt<n){ cout<<"orz"<<endl; return 0; } cout<<ans<<endl; return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define ll long long #define maxn 200010 using namespace std; template<typename T> inline void read(T &x){ x=0; bool flag=0; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) flag=1; for(;isdigit(c);c=getchar()) x=x*10+(c^48); if(flag) x=-x; } int n,m,x,y,z,t,ans=0,d[maxn],a[20010][20010]; bool visit[maxn]; void prim(){ memset(d,0x3f,sizeof(d)); d[1]=0; for(int i=1;i<=n;i++){ t=0; for(int j=1;j<=n;j++) if((!visit[j])&&(t==0||d[j]<d[t])) t=j; visit[t]=true; for(int s=1;s<=n;s++) if(!visit[s]) d[s]=min(d[s],a[t][s]); } } int main(){ read(n);read(m); memset(a,0x3f,sizeof(a)); for(int i=1;i<=n;i++) a[i][i]=0; for(int i=1;i<=m;i++){ read(x),read(y),read(z); a[y][x]=a[x][y]=min(a[x][y],z); } prim(); for(int i=2;i<=n;i++) ans+=d[i]; cout<<ans<<endl; return 0; }
虽然code2用邻接矩阵存看起来会比code1好懂亿点点,但是n和m稍微大点它就MLE了啊QAQ
(以下内容来自课件)
? 特化算法,用来针对一些奇怪的最小生成树问题
? 1.找到距离每个点最近的点
? 2.使用并查集维护连通性并缩点
? 3.重复上述步骤,只不过这次对于每个子树找到离他最近的点
? 由于每次循环联通块数目至少折半,因此复杂度是对的
? 适用于解决最小异或生成树这样的棘手问题
原文:https://www.cnblogs.com/DReamLion/p/14387033.html