[关闭]
@zhshh 2018-07-17T12:50:05.000000Z 字数 1933 阅读 837

图论-最小生成树

@zhshh OI cpp 图论


存图就不说了。。

最小树模板题目P3366 【模板】最小生成树

算法

kruskal

思路

排序,用并查集维护树,使得在同一棵树上的两个节点不会自己相连。(树1和树2可以相连?这就相当于是把两个树合并了)

代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #define MX 1000010
  4. using namespace std;
  5. struct node{
  6. int p1,p2,w;
  7. }a[MX];
  8. int fa[MX];
  9. int find(int x){
  10. return fa[x]==x?x:fa[x]=find(fa[x]);
  11. }
  12. void un(int x,int y){
  13. fa[find(x)]=find(y);
  14. }
  15. int operator<(node a,node b){
  16. return a.w<b.w;
  17. }
  18. int n,m;
  19. void init(){
  20. cin>>n>>m;
  21. for(int i=1;i<=m;i++){
  22. cin>>a[i].p1>>a[i].p2>>a[i].w;
  23. }
  24. for(int i=1;i<=n;i++){
  25. fa[i]=i;
  26. }
  27. }
  28. void work(){
  29. sort(a+1,a+m+1);
  30. int ans=0,tot=0;
  31. for(int i=1;i<=m;i++){
  32. if(find(a[i].p1)!=find(a[i].p2)){
  33. un(a[i].p1,a[i].p2);
  34. ans+=a[i].w;
  35. tot++;
  36. }
  37. if(tot==n-1)break;
  38. }
  39. if(tot==n-1){
  40. cout<<ans<<endl;
  41. }else{
  42. cout<<"orz";
  43. }
  44. }
  45. int main(){
  46. init();
  47. work();
  48. }

prim

回到顶部
这个和dijkstra形似。。详见最短路文对比

思路

n次循环,每次都选择当前最小dis的minj扩展这棵树。之后把与minj相连的所有节点更新为距离树最近的dis
由于kruskal是基于边权的排序,所以没有方向,而prim是有明确起点终点的前向星存图,所以必须要正反存图

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #define INF 0x3f3f3f3f
  5. #define MX 1000010
  6. using namespace std;
  7. struct node{
  8. int nxt;
  9. int v;
  10. int w;
  11. }edge[MX];
  12. int tot,ans,n,m;
  13. int head[MX];
  14. int dis[MX];
  15. int vis[MX]={0};
  16. void add(int u,int v,int w){
  17. tot++;
  18. edge[tot].v=v;
  19. edge[tot].w=w;
  20. edge[tot].nxt=head[u];
  21. head[u]=tot;
  22. }
  23. void init(){
  24. memset(head,-1,sizeof(head));
  25. cin>>n>>m;
  26. int a,b,c;
  27. for(int i=1;i<=m;i++){
  28. cin>>a>>b>>c;
  29. add(a,b,c);//正反存图
  30. add(b,a,c);
  31. }
  32. }
  33. void prim(){
  34. memset(dis,0x3f,sizeof(dis));
  35. dis[1]=0;
  36. int minj,mindis;
  37. for(int i=1;i<=n;i++){
  38. minj=-1;
  39. mindis=INF;
  40. for(int j=1;j<=n;j++){//循环每个节点,从而找到dis最小的节点(稍后说明dis)
  41. if(!vis[j] && dis[j]<mindis){
  42. minj=j;
  43. mindis=dis[j];
  44. }
  45. }
  46. if(minj==-1){//正常情况下(连通图)应该是n次恰好找完n个节点。如果是-1,没找完。。说明有不连通
  47. cout<<"orz"<<endl;
  48. return ;
  49. }
  50. vis[minj]=1;//上面选择的最优节点。登记vis,记录ans
  51. ans+=dis[minj];
  52. for(int j=head[minj];j!=-1;j=edge[j].nxt){//依次更新和minj这个节点相连的所有节点
  53. int v=edge[j].v;
  54. if(!vis[v] && dis[v]>edge[j].w){//由于树内等价,所以这里更新为距离树的最小距离,其中!vis[v]写不写都行
  55. dis[v]=edge[j].w;//和树的距离,因为minj在树上,因此更新为min(dis[v],edge[j].w)
  56. }//区别于dijkstra,由于要看从起点开始的最短路,因此动态更新为最短距离
  57. }
  58. }
  59. cout<<ans;
  60. }
  61. void work(){
  62. prim();
  63. }
  64. int main(){
  65. init();
  66. work();
  67. }

题目

回到顶部

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注