最小生成树

来源:互联网 发布:北京学软件测试 编辑:程序博客网 时间:2024/06/06 01:40

求最小生成树是指找出覆盖图中所有点的边集(生成树)中,所有边权之和最小的一个。常用的算法有prim算法和kruskal算法;基本上都是贪心的思想,prim算法类似于dijkstra算法。

prim()算法:时间复杂度为V^2(V为节点数数目),适用于稠密图,当用优先队列优化时,时间复杂度为E*log(V)(E为边的数目),适用于稀疏图。prim的思想是:初始化最小生成树集合为空,任取一点为最小生成树根节点,加入最小生成树集合,不断的往已经生成最小生成树中加节点(原则是与1、加入当前与已生成的最小生成树集合相连;2、没有被加入集合中,3、满足以上两个条件的最短边)循环操作,知道所有点都加入生成树集合中;

kruskal算法:时间复杂度为E*log(E)(E为边数)。kruskal算法的思想是:将图中的边权值按照从小到大排序,如果选出的边不能使当前已经确定的最小生成树部分形成回路(使用并查集判断),则其可以为最小生成树的部分加入集合,否则舍去,至到加入了n-1条边为止。


最小生成树的性质:

(1)、最小生成树的最长k-1条边都去掉后,正好将原图分成了k个连通分支。
(2)、一个图的两棵最小生成树,边的权值序列排序后结果相同。

poj1258最小生成树模板题目,由于数据比较小,prim和kruskal都可以做;


prim算法(临接矩阵存图);

#include<cstdio>#include<cstring>#include<cmath>#include<climits>#include<cctype>#include<climits>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<string>#include<set>#include<stack>#include<map>#define ll long long#define MAX 110#define INF 2000000000#define eps 1e-8using namespace std;int a[MAX][MAX],n;int prim(int s){      //以s为跟求最小生成树; int d[MAX],vis[MAX],ans = 0;     //d记录当前节点; memset(vis,0,sizeof(vis));       //记录节点是否已经在最小生成树中 for (int i=0; i<n; i++) d[i] = (i == s ? 0 : INF);for (int i=0; i<n; i++){          //注意此处是0到n,有时是0到n-1,主要看d数组初始化; int minn = INF,x;for (int j=0; j<n; j++){if (!vis[j] && d[j] < minn){minn = d[j];x = j;}}ans += d[x];vis[x] = 1;for (int j=0; j<n; j++){if (!vis[j] && a[x][j]) d[j] = min(d[j],a[x][j]);}}return ans;} int main(){while (scanf("%d",&n) != EOF){for (int i=0; i<n; i++){for (int j=0; j<n; j++){scanf("%d",&a[i][j]);}}printf("%d\n",prim(0));}return 0;}


prim实现(邻接表存图+堆优化);

#include<cstdio>#include<cstring>#include<cmath>#include<climits>#include<cctype>#include<climits>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<string>#include<set>#include<stack>#include<map>#define ll long long#define MAX 1000#define INF 2000000000#define eps 1e-8using namespace std;struct Edge{                //定义边结构:起点,终点,权值int from,to,dist;};struct Node{                //利用对优化,记录当前节点到已生成的最小生成树的距离int u,w;bool operator < (const Node& rhs) const{return rhs.w < w;}};vector<int>G[MAX];    //记录与节点i相连的边的编号vector<Edge>edges;    //给边编号,并保存int n,vis[MAX],d[MAX];   void init(){memset(vis,0,sizeof(vis));for (int i=0; i<=n; i++) G[i].clear();edges.clear();}void addEdge(int from, int to, int dist){edges.push_back((Edge){from,to,dist});int k = edges.size();G[from].push_back(k-1);}int prim(int s){int ans = 0;priority_queue<Node>q;q.push((Node){s,0});for (int i=0; i<=n; i++) d[i] = (i == s ? 0 : INF);while (!q.empty()){Node t = q.top();q.pop();if (vis[t.u]) continue;//printf("u = %d  w = %d\n",t.u,t.w);vis[t.u] = 1;ans += t.w;int v = t.u;for (int i=0; i<G[v].size(); i++){Edge e = edges[G[v][i]];if (!vis[e.to]){d[e.to] = min(d[e.to],e.dist);q.push((Node){e.to,d[e.to]});}}}return ans;}int main(){while (scanf("%d",&n) != EOF){int temp;init();for (int i=0; i<n; i++){for (int j=0; j<n; j++){scanf("%d",&temp);if (i == j) continue;addEdge(i,j,temp);}}printf("%d\n",prim(0));}return 0;}

prim算法,模拟vectro<int>G[MAX]存图;

#include<cstdio>#include<cstring>#include<cmath>#include<climits>#include<cctype>#include<climits>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<string>#include<set>#include<stack>#include<map>#define ll long long#define MAX 1000000#define MAXN 110#define INF 2000000000#define eps 1e-8using namespace std;struct Edge{int from,to,dist;};struct Node{int u,w;bool operator < (const Node& rhs) const{return rhs.w < w;}};int first[MAX],next[MAX];vector<Edge>edges;void init(){memset(first,-1,sizeof(first));  //first[i],记录节点i的第一条边的编号,next[i]编号为i的边的下一条边的编号memset(next,-1,sizeof(next));edges.clear();}void addEdge(int from, int to, int dist){  //插入一条边,每次都插在第一条边的位置edges.push_back((Edge){from,to,dist});int k = edges.size();next[k-1] = first[from];first[from] = k-1;}int n;int prim(int s){int vis[MAXN],d[MAXN];int ans = 0;priority_queue<Node>q;memset(vis,0,sizeof(vis));for (int i=0; i<n; i++) d[i] = (i == s ? 0 : INF);q.push((Node){s,0});while (!q.empty()){Node t = q.top();q.pop();int v = t.u;if (vis[v]) continue;vis[v] = 1;ans += t.w;for (int i = first[v]; i != -1; i = next[i]){Edge e = edges[i];if (!vis[e.to]){d[e.to] = min(d[e.to], e.dist);q.push((Node){e.to,d[e.to]});}}}return ans;}int main(){while (scanf("%d",&n) != EOF){int temp;init();for (int i=0; i<n; i++){for (int j=0; j<n; j++){scanf("%d",&temp);if (i == j) continue;addEdge(i,j,temp);}}printf("%d\n",prim(0));}return 0;}

kruskal实现;

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<climits>#include<cctype>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<map>#include<set>#include<string>#include<stack>#define ll long long#define MAX 1000000#define INF INT_MAX#define eps 1e-8using namespace std;int fa[MAX],u[MAX],v[MAX],w[MAX],c[MAX];  //u,v,w记录边的起点,终点,和权值int n,m;int cmp(const int i, const int j){      //将边按照权值大小排序return w[i] < w[j];}void init(){for (int i=0; i<=m; i++) {fa[i] = i;c[i] = i;}}int find(int x){                      //并查集,用于判断当前边是否需要加入return x == fa[x] ? x : fa[x] = find(fa[x]);}int kruskal(){int cnt = 0,ans = 0;for (int i=0; i<m; i++){int e = c[i];int x = find(u[e]);int y = find(v[e]);if (x != y){ans += w[e];fa[x] = y;cnt++;if (cnt >= n-1) return ans; }}}int main(){while (scanf("%d",&n) != EOF){int temp;m = -1;for (int i=0; i<n; i++)for (int j=0; j<n; j++){scanf("%d",&temp);if (i == j) continue;u[++m] = i;v[m] = j;w[m] = temp;}init();sort(c,c+m+1,cmp);printf("%d\n",kruskal());}return 0;}                                                 

poj2349  最小生成树性质的应用;

#include<cstdio>#include<cstring>#include<cmath>#include<climits>#include<cctype>#include<climits>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<string>#include<set>#include<stack>#include<map>#define ll long long#define MAX 1000#define INF 1e10#define eps 1e-8using namespace std;bool cmp(const double x, const double y){return x > y;}struct Edge{int from,to;double dist;};struct Node{int u;double w;bool operator < (const Node& rhs) const{return rhs.w < w;}};vector<int>G[MAX];vector<Edge>edges;int n,S,vis[MAX];void init(){memset(vis,0,sizeof(vis));for (int i=0; i<=n; i++) G[i].clear();edges.clear();}void addEdge(int from, int to, double dist){edges.push_back((Edge){from,to,dist});int k = edges.size();G[from].push_back(k-1);}double prim(int s){double ans[MAX];double d[MAX];int cnt = 0;priority_queue<Node>q;q.push((Node){s,0});for (int i=0; i<=n; i++) d[i] = (i == s ? 0 : INF);while (!q.empty()){Node t = q.top();q.pop();if (vis[t.u]) continue;vis[t.u] = 1;ans[cnt++] = t.w;int v = t.u;for (int i=0; i<G[v].size(); i++){Edge e = edges[G[v][i]];if (!vis[e.to]){d[e.to] = min(d[e.to],e.dist);q.push((Node){e.to,d[e.to]});}}}sort(ans,ans+cnt,cmp);return ans[S-1];}double Dist(double x1, double y1, double x2, double y2){return sqrt((double)((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))) + eps;}int x[MAX],y[MAX];int main(){int T;scanf("%d",&T);while (T--){scanf("%d%d",&S,&n);init();for (int i=0; i<n; i++){scanf("%d%d",&x[i],&y[i]);}for (int i=0; i<n; i++){for (int j=0; j<n; j++){if (i == j) continue;addEdge(i,j,Dist(x[i],y[i],x[j],y[j]));}}printf("%.2f\n",prim(0)+eps);        //注意选用g++或gcc时double类型要用%f输出而不能用%lf,可能是精度问题;}return 0;}


0 0