• Algorithm Design——最小生成树


    本文主要介绍了最小生成树的两种解决办法:kruskal以及prim 

      1 /**
      2 Kruskal算法流程:
      3     (1)初始时所有节点属于孤立的集合;
      4     (2)按照边权递增顺序遍历所有的边,若遍历到的边两个节点仍分属于不同的
      5     集合(该边即为联通这两个结合的边中权值最小的那条),则确定该边为最小生
      6     成树上的一条边,并将这两个顶点分属的集合合并;
      7     (3)遍历完所有的边之后,原图上所有节点属于同一个集合则被选取的边和原图
      8     中所有节点构成最小生成树;否则原图不连通,最小生成树不存在。
      9 
     10 
     11 样例输入: 
     12 3
     13 1 2 1 
     14 1 3 2 
     15 2 3 4 
     16 4
     17 1 2 1 
     18 1 3 4 
     19 1 4 1 
     20 2 3 3
     21 2 4 2 
     22 3 4 5 
     23 0
     24 样例输出: 
     25 3 
     26 5
     27 */
     28 
     29 #include<cstdio>
     30 #include<algorithm>
     31 using namespace std;
     32 
     33 #define N  101
     34 
     35 int Tree[N];
     36 
     37 int findRoot(int x)//找到代表集合的树的根节点
     38 {
     39     if(Tree[x] == -1)
     40         return x;
     41     else
     42     {
     43         int tmp = findRoot(Tree[x]);
     44         Tree[x] = tmp;//x直接指向根节点
     45         return tmp;
     46     }
     47 }
     48 
     49 struct Edge//边结构体
     50 {
     51     int a,b;//边两个顶点的编号
     52     int cost;//该边的权值
     53 
     54     bool operator < (const Edge &A) const//重载<运算符
     55     {
     56         return cost < A.cost;
     57     }
     58 }edge[6000];
     59 
     60 int main()
     61 {
     62     int n;
     63     while(scanf_s("%d", &n) != EOF && n != 0)
     64     {
     65         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)
     66         {
     67             scanf_s("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].cost);//输入边的顶点以及权值
     68         }
     69 
     70         sort(edge + 1, edge + 1 + n * (n - 1) / 2);//按照边权值递增排序所有的边
     71 
     72         for(int i = 1 ; i <= n ; i ++)
     73         {
     74             Tree[i] = -1;//初始时所有的节点都是孤立的集合
     75         }
     76 
     77         int ans = 0;//最小生成树上边权值的和,初始值是0
     78         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)//按照权值递增顺序遍历所有的边
     79         {
     80             int a = findRoot(edge[i].a);
     81             int b = findRoot(edge[i].b);//查找该边两个顶点的信息结合
     82 
     83             if(a != b)//若它们属于不同的集合,则选用该边
     84             {
     85                 Tree[a] = b;//合并两个集合
     86                 ans += edge[i].cost;//累加该边权值
     87             }
     88         }
     89 
     90         printf_s("%d
    ", ans);//输出
     91     }
     92 }
     93 
     94  /**
     95 prim算法流程:
     96 (1)设置两个集合V和S,任意选择一个顶点作为起始顶点,将起始顶点放入集合S,
     97 其余顶点放入集合V;
     98 (2)然后使用贪心策略,选择一条长度最短并且顶点分别在S和V中的边(即为最小
     99 生成树中的一条边),将这条边在V中的顶点加入到集合S中;
    100 (3)循环执行(2),直到S中包含了所有顶点。
    101 
    102 样例输入: 
    103 3
    104 1 2 1 
    105 1 3 2 
    106 2 3 4 
    107 4
    108 1 2 1 
    109 1 3 4 
    110 1 4 1 
    111 2 3 3
    112 2 4 2 
    113 3 4 5 
    114 0
    115 样例输出: 
    116 3 
    117 5
    118 */
    119 
    120 #include<cstdio>
    121 #include<cstring>
    122 using namespace std;
    123 
    124 #define MaxInt 0x3f3f3f3f
    125 #define N 110
    126 int n;
    127 
    128 int map[N][N];//存放图
    129 int low[N];//存放每两个节点之间最小权值
    130 int visited[N];//标记某节点是否已经被访问
    131 
    132 int prim()
    133 {
    134     int pos, min, result = 0;
    135     memset(visited, 0, sizeof(visited));
    136 
    137     //从某点开始,分别标记和记录该点
    138     visited[1] = 1;
    139     pos = 1;
    140 
    141     for(int i = 1; i <= n ; i ++)
    142     {
    143         if(i != pos)
    144             low[i] = map[pos][i];
    145     }
    146 
    147     for(int i = 1; i < n; i ++)
    148     {
    149         min = MaxInt;
    150         //找到最小生成树节点集合K与剩余节点集合之间距离最小的点
    151         for(int j = 1; j <= n; j ++)
    152         {
    153             if(visited[j] == 0 && min > low[j])
    154             {
    155                 min = low[j];
    156                 pos = j;
    157             }
    158         }
    159 
    160         result += min;//最小权值累加
    161         visited[pos] = 1;//标记该点
    162         //更新权值
    163         for(int j = 1 ; j <= n ; j ++)
    164         {
    165             if(visited[j] == 0 && low[j] > map[pos][j])
    166                 low[j] = map[pos][j];
    167         }
    168     }
    169 
    170     return result;
    171 }
    172 
    173 int main()
    174 {
    175     int ans;
    176 
    177     while(scanf_s("%d", &n) != EOF && n != 0)
    178     {
    179         memset(map, 0, sizeof(map));
    180         int a, b, c;
    181         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)
    182         {
    183             scanf_s("%d%d%d", &a, &b, &c);//输入边的顶点以及权值
    184             map[a][b] = c;
    185             map[b][a] = c;
    186         }
    187 
    188         for(int i = 1 ; i <= n ; i ++)
    189         {
    190             for(int j = 1 ; j <= n ; j ++)
    191             {
    192                 if(i == j)
    193                     map[i][j] = 0;
    194                 else if(map[i][j] == 0)
    195                     map[i][j] = MaxInt;
    196             }
    197         }
    198 
    199         ans = prim();
    200         printf_s("%d
    ", ans);
    201     }
    202 }
  • 相关阅读:
    The FLARE On Challenge
    CVE-2013-2551漏洞成因与利用分析(ISCC2014 PWN6)
    CVE-2014-0322漏洞成因与利用分析
    CVE-2013-3897漏洞成因与利用分析
    译:《深入解析WINDOWS VISTA APC》——PART 1
    MemoryInjector 无痕注入
    一个APC引起的折腾 之题外记
    一个APC引起的折腾
    2020
    javascriptcore调试笔记
  • 原文地址:https://www.cnblogs.com/yiyi-xuechen/p/3452303.html
Copyright © 2020-2023  润新知