题解P3366 最小生成树

hicc0305

2018-04-22 15:19:36

Personal

## 1.Kruskal 先对所有的边进行排序,取最小的边。把以取的点通过并查集并起来,如果一条边的两个点已经在并查集内,则选这条边会导致树变成图。所以并查集就是用来判断能否加边的。 ```cpp #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m; struct edge { int x,y,z; }e[200011]; int f[5010]; bool cmp(edge a,edge b) { return a.z<b.z; } int find(int x) { if(f[x]!=x) f[x]=find(f[x]); return f[x]; } int main() { cin>>n>>m; for(int i=1;i<=m;i++) cin>>e[i].x>>e[i].y>>e[i].z; sort(e+1,e+m+1,cmp); int cnt=0,sum=0; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) { int u=e[i].x,v=e[i].y; if(find(u)==find(v)) continue; sum+=e[i].z; cnt++; f[find(u)]=find(v); if(cnt==n-1) break; } cout<<sum; return 0; } ``` ## 2.Prim prim适用于稠密的图。用dis[i]记录i到最小生成树这个集合的最小距离,从一个点开始扩展,不断的将最近的点加进这个集合。加进去之后再通过这个点去更新其他点的dis。类似于dijstra ```cpp #include<map> #include<vector> #include<list> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m,ans=0,num=1; int cnt=0; int f[5010],dis[5010],a[5010][5010]; void prime() { int l=0,r=1,u=1; dis[1]=0,f[1]=1; while(num<n) { int minn=0x7fffff; num++; for(int i=1;i<=n;i++) { if(dis[i]>a[u][i] && !f[i]) { dis[i]=a[u][i];//更新其他点的距离 } } for(int i=1;i<=n;i++) { if(!f[i] && dis[i]<minn) { minn=dis[i]; u=i;//取距离最小的一个点再次更新 } } ans+=minn; f[u]=1; } } int main() { memset(dis,0x7f,sizeof(dis)); memset(a,0x7f,sizeof(a)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if(a[x][y]>z) a[x][y]=z,a[y][x]=z; } prime(); if(num==n) cout<<ans; else cout<<"orz"; return 0; } ``` 加上堆优化(也就是优先队列): 此处引用的是一大佬代码,自己懒得打了 ```cpp #include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; int k,n,m,cnt,sum,ai,bi,ci,head[5005],dis[5005],vis[5005]; struct Edge { int v,w,next; }e[400005]; void add(int u,int v,int w) { e[++k].v=v; e[k].w=w; e[k].next=head[u]; head[u]=k; } typedef pair <int,int> pii; priority_queue <pii,vector<pii>,greater<pii> > q;//优先队列! void prim() { dis[1]=0; q.push(make_pair(0,1)); while(!q.empty()&&cnt<n) { int d=q.top().first,u=q.top().second; q.pop(); if(vis[u]) continue; cnt++; sum+=d; vis[u]=1; for(int i=head[u];i!=-1;i=e[i].next) if(e[i].w<dis[e[i].v]) dis[e[i].v]=e[i].w,q.push(make_pair(dis[e[i].v],e[i].v)); } } int main() { memset(dis,0x7f,sizeof(dis)); memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&ai,&bi,&ci); add(ai,bi,ci); add(bi,ai,ci); } prim(); if(cnt==n) printf("%d",sum); else printf("orz"); } ```