[入门]最小生成树--poj1258 Agi-Net
2014-07-25 21:07
218 查看
本题题意就是约翰成了村里的头头,然后他感觉很开森,就要把网共享给村名.但是拉网线甚么的是要成本的.然后题目给出一个矩阵,矩阵的某一个元素(i,j)就表示第i个村民到第j个村民那儿拉网线的成本,所以从样例就可以看出(i,j)是等于(j,i)的,然后(i,i)都是0,也就是自己到自己家不需要成本.
生成树的定义神马的百度百科里可以了解.大概就是一个图的子图如果满足连通性,也就是两两点之间都能连通,然后还没有环,它奏是棵树了,美其名曰生成树,其中边权和最小(对应实际问题中的总路程最短,费用最少等等)的生成树就叫最小生成树.求最小生成树有两种算法,其一是Prim算法,其二是Kruskal算法.我分别用两种方法做了一下.
对于图论问题,见图,算法解题,貌似都带一些模式化,比如prim算法用哪种存图方法会更方便操作等等,但是其中的思想在以后学到自己设计算法的份上时价值非凡..
这是Prim算法的代码:
还有Kruskal
Prim算法和Djistera在求最短路的时候的过程类似,是从某个顶点出发,然后不断加边的过程.当然,Djistera求最短路是找d值最小的顶点,而Prim则是找cost最小的边.
Kruskal算法每次从剩余边中取出最短的,加到集合X中(初始时集合为空,然后不断加边),但是因为不能取到那种成环的边,举个例子a-b,b-a两条边,那如果a-b已经加进去了,就不能再加b-a了.事实上也是如此,如果最终集合X中有环,我们显然可以把这个环断开,取出环上的某条边,剩下的点依然连通,从而说明如果最后X集合中的边存在环,就一定不是最小生成树了.所以不能加进环,那怎么判断我待选取的边是不是和之前的边构成环呢?那自然就可以用并查集,V个节点,每加一条边ab,我们就合并节点a,b,这样在整个并查集看来,这a,b同"父亲"(father[a]
= father[b]),他们就好像一个点一样.比如有4个点的这个图:1-2, 1-3, 1-4, 2-3, 2-4 假设我们把1-2,1-3,1-4加进去了,他们在并查集中就像一个点一样(不管怎么合并,此时一定有f[1] = f[2] = f[3] = f[4])我们判断2-3该不该加时,由于f[2] = f[3],我们就可以知道,当前加进去的边已经可以让2和3连通了,所以就跳过找下一条边了~~~Kruskal算法总体上比Prim优秀,因为不需要堆优化就已经有O(|E|*log|V|)的复杂度了,而前者没有优化前是O(|V|^2)的.相比较之下,前者代码量又会小一些,所以怎么取舍,自己看吧...
生成树的定义神马的百度百科里可以了解.大概就是一个图的子图如果满足连通性,也就是两两点之间都能连通,然后还没有环,它奏是棵树了,美其名曰生成树,其中边权和最小(对应实际问题中的总路程最短,费用最少等等)的生成树就叫最小生成树.求最小生成树有两种算法,其一是Prim算法,其二是Kruskal算法.我分别用两种方法做了一下.
对于图论问题,见图,算法解题,貌似都带一些模式化,比如prim算法用哪种存图方法会更方便操作等等,但是其中的思想在以后学到自己设计算法的份上时价值非凡..
这是Prim算法的代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <climits> //INT_MAX using namespace std; const int MAX = 128; const int INF = INT_MAX>>1; int cost[MAX][MAX];//cost[u][v]表示边e=(u,v)的权值(不存在时为INF) int mincost[MAX];//从集合X出发的边到每个顶点的最小权值 bool used[MAX]; //顶点i是否包含在集合X中 int V, E; int prim(void) { fill(mincost, mincost+V, INF); memset(used, false, sizeof(used)); mincost[0] = 0; int res = 0; while (true) { int v = -1; for (int u = 0; u < V; ++u) { if (!used[u] && (v == -1 || mincost[u] < mincost[v])) v = u; } if (v == -1) break; //结束 used[v] = true; res += mincost[v]; for (int u = 0; u < V; ++u) { mincost[u] = min(mincost[u], cost[v][u]); } } return res; } int main() { while (~scanf(" %d", &V)) { for (int i = 0; i < V; ++i) fill(cost[i], cost[i]+V, INF); for (int i = 0; i < V; ++i) { for (int j = 0; j < V; ++j) { scanf(" %d", &cost[i][j]); } }//cin printf("%d\n", prim()); } return 0; }
还有Kruskal
/* * 从小到大查看边,如果不产生环,加把这条边加进生成树中 */ #include <cstdio> #include <cstring> #include <algorithm> #include <climits> using namespace std; const int MAX = 110; const int INF = INT_MAX>>2; struct edge { int u, v, cost; }; edge es[MAX*MAX]; int V, E; int father[MAX]; /* 并查集 */ int find(int x) { if (father[x] == x) return x; return father[x] = find(father[x]); } bool same(int x, int y) { return find(x) == find(y); } void unite(int x, int y) { int f1 = find(x), f2 = find(y); if (f1 == f2) return; //rank is not necessary father[f1] = f2; } void init_set(int max_f) { for (register int i = 0; i < max_f; ++i) father[i] = i; } bool cmp(edge e1, edge e2) { return e1.cost < e2.cost; } /* Kruskal */ int kruskal(void) { sort(es, es+E, cmp); init_set(V); int res = 0; for (int i = 0; i < E; ++i) { edge e = es[i]; if (!same(e.u, e.v)) { unite(e.u, e.v); res += e.cost; } } return res; } int main() { while (~scanf(" %d", &V)) { E = 0; int cost; for (int i = 0; i < V; ++i) { for (int j = 0; j < V; ++j) { scanf(" %d", &cost); if (j > i) continue; es[E].u = i, es[E].v = j, es[E].cost = cost;++E; //es[++E].u = j, es[E].v = i, es[E].cost = cost; } } printf("%d\n", kruskal()); } return 0; }
Prim算法和Djistera在求最短路的时候的过程类似,是从某个顶点出发,然后不断加边的过程.当然,Djistera求最短路是找d值最小的顶点,而Prim则是找cost最小的边.
Kruskal算法每次从剩余边中取出最短的,加到集合X中(初始时集合为空,然后不断加边),但是因为不能取到那种成环的边,举个例子a-b,b-a两条边,那如果a-b已经加进去了,就不能再加b-a了.事实上也是如此,如果最终集合X中有环,我们显然可以把这个环断开,取出环上的某条边,剩下的点依然连通,从而说明如果最后X集合中的边存在环,就一定不是最小生成树了.所以不能加进环,那怎么判断我待选取的边是不是和之前的边构成环呢?那自然就可以用并查集,V个节点,每加一条边ab,我们就合并节点a,b,这样在整个并查集看来,这a,b同"父亲"(father[a]
= father[b]),他们就好像一个点一样.比如有4个点的这个图:1-2, 1-3, 1-4, 2-3, 2-4 假设我们把1-2,1-3,1-4加进去了,他们在并查集中就像一个点一样(不管怎么合并,此时一定有f[1] = f[2] = f[3] = f[4])我们判断2-3该不该加时,由于f[2] = f[3],我们就可以知道,当前加进去的边已经可以让2和3连通了,所以就跳过找下一条边了~~~Kruskal算法总体上比Prim优秀,因为不需要堆优化就已经有O(|E|*log|V|)的复杂度了,而前者没有优化前是O(|V|^2)的.相比较之下,前者代码量又会小一些,所以怎么取舍,自己看吧...
相关文章推荐
- POJ 1258 Agri-Net 【最小生成树入门题目】
- POJ 1258 Agri-Net 【最小生成树入门题目】
- POJ 1258 Agri-Net 最小生成树(Kruskal算法入门)
- POJ 1258 Agri-Net (最小生成树入门题目)
- POJ 1258 Agri-Net 【最小生成树入门题目】【prime模板】
- POJ 1258 Agri-Net 【最小生成树入门题目】
- Poj 1258 Agri-Net[MST最小生成树]
- poj 1258 Agri-Net 最小生成树 kruskal
- POJ 1258 Agri-Net(最小生成树,基础)
- 初级->图算法->最小生成树 poj 1258 Agri-Net
- poj 1258 Agri-Net(最小生成树)
- Poj1258_Agri-Net(最小生成树)
- POJ 1258 Agri-Net(最小生成树 Prim 模版题)
- POJ 1258 Agri-Net 【最小生成树】
- poj 1258 Agri-Net 最小生成树
- poj 1258 Agri-Net(最小生成树模板代码)
- poj1258 Agri-Net (最小生成树prime)
- poj 1258 Agri-Net 最小生成树 kruskal
- POJ 1258 Agri-Net 最小生成树 Prim
- POJ1258 Agri-Net 最小生成树 C语言