POJ1258 Agri-Net(最小生成树)

来源:互联网 发布:php在线运行代码 编辑:程序博客网 时间:2024/05/11 17:36

题意:

市长要将每个村庄都连通起来,要求村庄之间路的总和最短

要点:

学了一下最小生成树,目前只会Kruskal算法,这个算法的基本思路是先按边的权值,从小到大排列,然后每次将边的起点和终点放进集合中,直至所有节点都在一个连通分量中。既然是连通分量,那就正好可以用刚学的并查集。如果这条边的两个节点通过find发现相同了,说明这两个已经在集合中了。最后并查集也可以看成一棵树,与我前面做过的一道题一样,只要边数为节点数-1即可,这也就是最小生成树。

参考博客:最小生成树


Kruskal算法:

15338787Seasonal1258Accepted240K16MSC++1001B2016-03-31 22:09:17

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<algorithm>using namespace std;struct edge{int u, v, len;}e[150*150];int p[150];int n,num;bool cmp(const edge &a, const edge &b){return a.len < b.len;//根据权值从小到大排列}void init(){for (int i = 0; i < n; i++)p[i] = i;}int find(int x){if (p[x] == x) return x;return p[x] = find(p[x]);}bool merge(int x, int y){x = find(x);y = find(y);if (x != y)//不相等说明无环图{p[x] = y;return true;}return false;}int kruskal(){int sum = 0, edges = 0;;init();sort(e, e + num, cmp);for (int i = 0; i < num; i++){if (merge(e[i].u, e[i].v))//将边按权值从小到大生成一棵树,利用并查集{sum += e[i].len;edges++;}if (edges + 1 == n)//树的边总数应是结点总数-1return sum;}return -1;}int main(){int temp,i,j;while (~scanf("%d", &n)){num = 0;//用num记录总共有几条边for (i = 0; i < n; ++i)for (j = 0; j < n; ++j){scanf("%d", &temp);if (i != j){e[num].u = i;e[num].v = j;//记录起点和终点e[num++].len = temp;//记录对应权值}}printf("%d\n",kruskal());}return 0;}


Prim算法:

15341439Seasonal1258Accepted208K16MSC++737B2016-04-01 19:58:47
#include<stdio.h>#include<stdlib.h>#include<string.h>#define INF 0xffffffint n;int map[105][105],low[105];bool vis[105];int prim(){memset(vis, false, sizeof(vis));int i, j, mark, min,sum=0;vis[1] = true;for (i = 1; i <= n; ++i)low[i] = map[1][i];for (i = 1; i < n; ++i){min = INF;for (j = 1; j <= n; ++j){if (!vis[j] && low[j]<min){min = low[j];mark = j;}}sum += min;vis[mark] = true;for (j = 1; j <= n; ++j)if (!vis[j] && map[mark][j] < low[j])low[j] = map[mark][j];}return sum;}int main(){while (~scanf("%d", &n)){for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)scanf("%d", &map[i][j]);printf("%d\n", prim());}return 0;}



0 0