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

  • 相关阅读:
    一个好的时间函数
    Codeforces 785E. Anton and Permutation
    Codeforces 785 D. Anton and School
    Codeforces 510 E. Fox And Dinner
    Codeforces 242 E. XOR on Segment
    Codeforces 629 E. Famil Door and Roads
    Codeforces 600E. Lomsat gelral(Dsu on tree学习)
    Codeforces 438D The Child and Sequence
    Codeforces 729E Subordinates
    【ATcoder】D
  • 原文地址:https://www.cnblogs.com/oscar-cnblogs/p/6403860.html
Copyright © 2011-2022 走看看