zoukankan      html  css  js  c++  java
  • 最小生成树求法 Prim + Kruskal

    prim算法的思路 和dijkstra是一样的

    每次选取一个最近的点 然后去向新的节点扩张 注意这里的扩张 不再是 以前求最短路时候的到新的节点的最短距离

    而是因为要生成一棵树 所以是要连一根最短的连枝 所以关键部分修改一下

    dist[u] = min(dist[u], e.cost) --->>e是连接 v 和 u的边

    同样地 普同写法O(v^2) 用队列优化后O(E*logV)

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <queue>
      5 #define MAXV 256
      6 #define MAXE 256
      7 #define INF 0x3f3f3f3f
      8 
      9 using namespace std;
     10 typedef pair<int,int> P;
     11 int V, E;
     12 //和dijkstra完全一样
     13 //书上写法 O(V^2)
     14 int graph[MAXV][MAXV];
     15 int prim()
     16 {
     17     int dist[MAXV];
     18     int res = 0;
     19     bool use[MAXV];
     20     fill(dist, dist+MAXV, INF);
     21     fill(use, use+MAXV, 0);
     22     dist[1] = 0;//假定1为源点
     23     while (true)
     24     {
     25         int v = -1;
     26         for (int i = 1; i <= V; i++)
     27         {
     28             if (!use[i] && (v == -1 || dist[i] < dist[v])) v = i;//查找离原点最近的点
     29         }
     30         if (v == -1) break;
     31         use[v] = true;
     32         res += dist[v];
     33         cout << dist[v] << endl; //打印树枝的情况
     34         for (int i = 1; i <= V; i++)
     35         {
     36             dist[i] = min(dist[i], graph[v][i]);//这里是唯一的区别  --->>.但其实 这个dist中的值 最后是有问题的 这样最后dist保存的 是离它最近的一个节点的边的值
     37             //第33行打印的结果之所以正确是因为 按着离原点最近的顺序 向外扩张 在dist改变之前已经打印了 但是打印之后它的值是可能会发生改变的
     38             /*
     39             3).重复下列操作,直到Vnew = V:
     40 
     41 a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,
     42 而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
     43 
     44 b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
     45             */
     46         }
     47     }
     48     return res;
     49 }
     50 
     51 //使用堆维护 再使用prim O(E*logV)
     52 struct Edge
     53 {
     54     int to, cost, next;
     55     Edge() {}
     56     Edge(int to, int cost, int next) : to(to), cost(cost), next(next) {}
     57 }edge[MAXE];
     58 int num = 0;
     59 int head[MAXV];
     60 void Add(int from, int to, int cost)
     61 {
     62     edge[num] = Edge(to, cost, head[from]);
     63     head[from] = num++;
     64 }
     65 
     66 int prim2()
     67 {
     68     int dist[MAXV], res = 0;
     69     bool use[MAXV];
     70     fill(use, use+MAXV, false);
     71     fill(dist, dist+MAXV, INF);
     72     priority_queue<P, vector<P>, greater<P> > que;
     73     dist[1] = 0;//假定1为源点
     74     que.push(P(dist[1], 1));//first -->距离 second -->编号
     75     while(!que.empty())
     76     {
     77         P p = que.top();
     78         que.pop();
     79         if (!use[p.second])//这个也是那个问题 先前点(作为现在去扩张的点 如果被打印过 就不再去打印它了 总之算法思路就是 从原点开始 让最近的一个点去扩张)
     80         {
     81             res += dist[p.second];
     82             cout << dist[p.second] << endl;
     83         }
     84         use[p.second] = true;
     85         if (dist[p.second] < p.first)
     86         {
     87             continue;
     88         }
     89         int t = head[p.second];
     90         while (t != -1)
     91         {
     92             Edge e = edge[t];
     93             if (!use[e.to] && dist[e.to] > e.cost)
     94             {
     95                 dist[e.to] = e.cost;
     96                 que.push(P(e.cost, e.to));
     97             }
     98             t = e.next;
     99         }
    100     }
    101     return res;
    102 }
    103 
    104 
    105 int main()
    106 {
    107     freopen("in.txt", "r", stdin);
    108     scanf("%d%d", &V, &E);
    109     memset(edge, -1, sizeof(edge));
    110     memset(head, -1, sizeof(head));
    111     for(int i = 1; i <= V; i++)
    112         for (int j = 1; j <= V; j++) graph[i][j] = INF;
    113     for (int i = 0; i < E; i++)
    114     {
    115         int from, to, cost;
    116         scanf("%d%d%d", &from, &to, &cost);
    117         graph[from][to] = cost;
    118         graph[to][from] = cost;
    119         Add(from, to, cost);
    120         Add(to, from, cost);
    121     }
    122     int ans = prim2();
    123     cout << ans << endl;
    124 }

    Kruskal -->> O(E*logV)  思路就更加的简单

    将所有的边 排序 

    贪心地从小到大取 如果连接进一个新的点 就加入树枝的集合中 最终 得到的边的集合就是最小生成树

    这里判断是否是新的节点 就是判断连通性 那么就使用并查集非常容易

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <algorithm>
     5 #define MAXV 256
     6 #define MAXE 256
     7 #define INF 0x3f3f3f3f
     8 using namespace std;
     9 
    10 int E, V;
    11 //Kruskal 思路: 按边的角度出发 将边从小到大排序 如果 from to 不再一个连通块中 就可以取这一条边(不会产生环)
    12 struct Edge
    13 {
    14     int from, to, cost;
    15     Edge(){}
    16     Edge(int from, int to, int cost) : from(from), to(to), cost(cost) {}
    17 }edge[MAXE];
    18 
    19 int par[MAXV];
    20 int find(int x)
    21 {
    22     if(x == par[x]) return x;
    23     else return par[x] = find(par[x]);
    24 }
    25 void unite(int x, int y)
    26 {
    27     int px = find(x), py = find(y);
    28     if (px == py) return ;
    29     else par[py] = px;
    30 }
    31 bool same(int x, int y)
    32 {
    33     int px = find(x), py = find(y);
    34     return px == py;
    35 }
    36 
    37 bool cmp(Edge e1, Edge e2)
    38 {
    39     return e1.cost < e2.cost;
    40 }
    41 
    42 //O(E*logV) -->>对于并查集的操作是logN
    43 int Kruskal()
    44 {
    45     int res = 0;
    46     for (int i = 1; i <= V; i++) par[i] = i;
    47     sort(edge, edge+E, cmp);
    48     for (int i = 0; i < E; i++)
    49     {
    50         Edge e = edge[i];
    51         if (!same(e.from, e.to))
    52         {
    53             cout << e.from << " " << e.to << " : " << e.cost << endl;
    54             res += e.cost;
    55             unite(e.from, e.to);
    56         }
    57     }
    58     return res;
    59 }
    60 
    61 int main()
    62 {
    63     freopen("in.txt", "r", stdin);
    64     scanf("%d%d", &V, &E);
    65     for (int i = 0; i < E; i++)
    66     {
    67         int from, to, cost;
    68         scanf("%d%d%d", &from, &to, &cost);
    69         edge[i] = Edge(from, to, cost);
    70     }
    71     int ans = Kruskal();
    72     cout << ans << endl;
    73     return 0;
    74 }

    转一篇写的很详细的博客

    http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html

  • 相关阅读:
    php的多态性
    php接口
    php抽象类和抽象方法
    php类与对象的魔术方法
    php关键字
    php类型之class类,对象,构造函数的理解
    php日期格式化
    php之常用字符串方法
    php将获取的数组变成字符串传入txt文本。。。
    PHP之键值操作函数
  • 原文地址:https://www.cnblogs.com/oscar-cnblogs/p/6403860.html
Copyright © 2011-2022 走看看