zoukankan      html  css  js  c++  java
  • [OI学习笔记]最小生成树之Prim算法

    背景

      今天遇到这样一道题:(洛谷2820

            某个局域网内有n(n<=100)台计算机,由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用f(i,j)表示i,j之间连接的畅通程度,f(i,j)值越小表示i,j之间连接越通畅,f(i,j)为0表示i,j之间无网线连接。

        输出Σf(i,j)的的最大值

        是最小生成树没错了,于是去温习了一遍图论。。。

        最小生成树定义(摘自百度百科):

             在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此的权重,若存在 T 为 E 的子集(即)且为无循环图,使得
    w(T) 最小,则此 T 为 G 的最小生成树。
            
    最小生成树其实是最小权重生成树的简称。 [1] 
          其实就是给你一个有n个点的无向联通图,每条边有一个选择的费用, 你需要只选择其中的n-1条边(因为n个点的联通图要保证无环最多只能有n-1条边),使得这个图仍然联通,保证不构成环,且费用和最小。
        以下面这张图为例:
            
     
        它的最小生成树当然是:
           
        实现最小生成树有两种算法,分别是Prim算法和Kruskal算法(因为还有蛋疼的开学考要准备,就先介绍一下Prim,过几天更Kruskal)
       

    Prim算法 

        贪心策略:

            1)把已经在树中的点全部装进集合A,其余装进集合B,这样就保证不构成环
            2)每次选取连接A集合与B集合的边中的最短的一条进行连接,这样保证了边权和最小
        实现方法:
            1)随便找一个起始点(反正最后都在树上),并装进A集合
            2)循环n-1次,其中每次:
                1)选取连接A集合与B集合的边中的最短的一条进行连接
                  2)将新加入的点加入A集合
            3)n-1次之后完成

        代码:(摘自模版题洛谷3366的题解)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define inf 0x7fffffff
     4 #define maxn 5005
     5 int cost[maxn][maxn],minn,n,m,v2[maxn],tot=1,now,ans;
     6 bool v1[maxn];
     7 inline void getcost()
     8 {
     9     scanf("%d%d",&n,&m);
    10     for(int i=1;i<=n;i++)
    11     {
    12         for(int j=1;j<=n;j++)
    13         {
    14             cost[i][j]=inf;
    15         }
    16     }
    17     //先将数组赋为极大值
    18     for(int i=1,u,v,w;i<=m;i++)
    19     {
    20         scanf("%d%d%d",&u,&v,&w);
    21         if(cost[u][v]>w)
    22         {
    23             cost[u][v]=cost[v][u]=w;
    24         }
    25     }
    26     //初始化cost数组
    27     for(int i=1;i<=n;i++)
    28     {
    29         v2[i]=cost[1][i];
    30     }
    31     v1[1]=1;
    32     //找出与1节点相连的边并进行标记
    33 }
    34 inline int prim()
    35 {
    36     while(tot<n)
    37     //最小生成树的概念,边数比点数小一
    38     {
    39         minn=inf; 
    40         //附上初值
    41         tot++;
    42         //已经使用边数++
    43         for(int i=1;i<=n;i++)
    44         {
    45             if(!v1[i]&&v2[i]<minn)
    46             {
    47                 minn=v2[i];
    48                 now=i;
    49             }
    50         }
    51         //找出与该点相连的最小边
    52         ans+=minn;
    53         //更新答案
    54         for(int i=1;i<=n;i++)
    55         {
    56             if(v2[i]>cost[now][i]&&!v1[i])
    57             {
    58                 v2[i]=cost[now][i];
    59             }
    60         }
    61         v1[now]=1;
    62         //在找出与now节点相连的边并进行标记
    63     }
    64     return ans;
    65 }
    66 int main()
    67 {
    68     getcost();
    69     printf("%d",prim());
    70     return 0;
    71 }

        再次以图一为例,

        输入:

    4 5
    1 2 40
    1 3 20
    1 4 30
    2 3 30
    4 3 10

         输出:

    60

        完全正确。

    本篇文章为SHINE_GEEK原创,转载请注明来源!
    written_by:SHINE_GEEK

    -------------------------------------
    签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
    -------------------------------------
  • 相关阅读:
    使用SQL语句对表进行分页查询
    [转]Message Crack Wizard for Win32 SDK Developer
    笔记——《C语言也能干大事》之对话框程序代码
    如何通过C#调用CHM帮助文件,显示到指定页面
    winform 数字输入控件初稿
    用JAVA在读取EXCEL文件时如何判断列隐藏
    css中height:100%不起作用的解决方法 项目个案
    枚举(enum)的常用操作
    转 VirtualBox“please use a kernel appropriate for your cpu”
    Asp.NET导出Excel文件乱码 终极解决方法
  • 原文地址:https://www.cnblogs.com/sjrb/p/9524741.html
Copyright © 2011-2022 走看看