HDU 1233 还是畅通工程(最小生成树 Prim+Kruskal)
来源:互联网 发布:流量互刷软件 编辑:程序博客网 时间:2024/05/17 01:09
原题地址
http://acm.hdu.edu.cn/showproblem.php?pid=1233
题意:(最小生成树裸题)有N个村庄,给出村庄两两之间的距离,要求铺设公路,使得任何两个村庄间都可以实现互通(不一定有直接的公路相连,只要能间接通过公路可达即可),计算最小的公路总长度。
解题思路
上一题《HDU 1232 畅通工程》考察的是并查集的应用,这一题考察了比较重要的最小生成树算法。
最小生成树算法有两种:Prim算法和Kruskal算法,见最小生成树算法讲解。
简单总结这两种算法:
- Prim算法从顶点的角度出发,每次选择距离当前集合最近的节点加入,直到所有节点都加入。该算法的时间复杂度为O(n²),与图中边数无关,该算法适合于稠密图。
- Kruskal算法从边的角度出发,每次总是选择权重最小的边加入,直到加入n-1条边为止。(如果加入一条边后出现回路,skip这条边)。该算法的时间主要取决于边数,它较适合于稀疏图。
AC代码
Prim算法:
由于这道题目给出了所有的边数,所以可以视为稠密图。
Prim算法复杂度O(n^2),耗时296ms。
PS:Prim算法可以进行堆优化,详见Prim算法 和 堆优化的Prim算法。
#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 105, INF = 0x3f3f3f;int n, map[maxn][maxn], dist[maxn]; //map[i][j]记录两顶点间距离,dist[j]记录j到V中顶点的最小值bool visited[maxn]; //顶点是否访问过(加入了连通集)void init() //初始化距离和访问情况{ for (int i = 1; i<=n; ++i) dist[i] = INF; memset(visited, false, sizeof(visited));}int prim() //Prim算法返回最小生成树权值之和{ int sum = 0; //以1为起点 for (int i = 2; i<=n; ++i) //更新1到所有顶点的距离 dist[i] = map[1][i]; visited[1] = true; for (int road = 0; road < n-1; ++road) //最终会生成n-1条道路 { int minn = INF, pos; for (int j = 1; j<=n; ++j) //寻找到连通集的最小边 { if (!visited[j] && dist[j] < minn) { minn = dist[j]; pos = j; //pos记录该顶点 } } visited[pos] = true; sum += dist[pos]; //加入该顶点 for (int j = 1; j<=n; ++j) //松弛 { if (!visited[j] && map[pos][j] < dist[j]) //更新连通集外顶点的最小距离 dist[j] = map[pos][j]; } } return sum;}int main(){ ios::sync_with_stdio(false); while (cin >> n && n) { init(); //初始化 int u, v, tmp, ans; for (int i = 0; i < n*(n-1)/2; ++i) { cin >> u >> v >> tmp; map[u][v] = map[v][u] = tmp; //顶点间距离赋值 } ans = prim(); cout << ans << endl; } return 0;}
Kruskal算法(贪心+并查集)
Kruskal算法复杂度O(E*logE),E为边数。
耗时:280ms(非递归并查集),327ms(递归并查集)
#include <iostream>#include <algorithm>using namespace std;const int maxn = 105;typedef struct node{ int pre, suc, w; bool operator < (const node & A) const { return w < A.w; }}Edge; //每条边的起始、终止和权重Edge edge[maxn*maxn/2]; //记录每条边int pre[maxn] = {0}; //记录每个顶点的上级void make_set(int n) //初始化并查集{ for (int i = 1; i <= n; ++i) pre[i] = i;}/*int find(int x) //递归找x的根节点{ if(pre[x] == x) return x; return pre[x] = find(pre[x]); //包含路径压缩}*/int find_root(int x) //非递归找x的根节点{ int r = x; while (pre[r] != r) r = pre[r]; //路径压缩 int i = x, j; while (i != r) { j = pre[i]; pre[i] = r; i = j; } return r;}int kruskal(int n, int num) //n顶点数, num边数{ sort(edge, edge+num); //按权重升序,注意是边数 make_set(n); //并查集初始化 int fx, fy; int sum = 0; for (int i = 0; i < num; ++i) { fx = find_root(edge[i].pre); fy = find_root(edge[i].suc); if (fx != fy) //如果不连通 { pre[fy] = fx; sum += edge[i].w; //将这条边加入 } } return sum;}int main(){ ios::sync_with_stdio(false); int n, num, ans; while(cin >> n && n) { num = (n * (n-1)) / 2; for (int i = 0; i < num; ++i) cin >> edge[i].pre >> edge[i].suc >> edge[i].w; ans = kruskal(n, num); cout << ans << endl; } return 0;}
0 0
- hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)
- hdu 1233 还是畅通工程(最小生成树,prim,kruskal)
- HDU 1233 还是畅通工程(最小生成树 Prim+Kruskal)
- hdoj 1233 还是畅通工程【最小生成树 kruskal && prim】
- HDOJ 1233 还是畅通工程 最小生成树 kruskal && prim
- HDU 1233 还是畅通工程(最小生成树Kruskal)
- HDU 1233 还是畅通工程 (最小生成树 Kruskal)
- HDU 1233 还是畅通工程(最小生成树kruskal)
- hdu 1233 还是畅通工程 (最小生成树,prim,优先队列,kruskal并查集)
- HDU 1233 还是畅通工程【最小生成树入门题,Kruskal算法+Prim算法】
- HDU 1233 还是畅通工程 最小生成树Kruskal算法和prim算法
- HDU 1233-还是畅通工程(经典的最小生成树, Kruskal和prim算法)
- hdu 还是畅通工程 (基础)(最小生成树)(Prim算法 && Kruskal算法)
- hdu1233还是畅通工程 最小生成树(prim或kruskal)
- hdu 1233 还是畅通工程(最小生成树Kruskal)
- hdu 1233 还是畅通工程(Prim最小生成树)
- hdu 1233 还是畅通工程--最小生成树prim
- HDU 1233 还是畅通工程---prim求最小生成树
- Android-Fragment(1)简单实现
- 文章标题
- AngularJS内置服务器--$http服务的简单用法
- 硬件常用术语
- 登录功能
- HDU 1233 还是畅通工程(最小生成树 Prim+Kruskal)
- javascript严格模式下的8点规则
- JAVA对数计算
- 第十一周(Dynamic ProgrammingV)
- C语言入门(一)
- Boyer Moore算法分析总结
- Bash循环处理带有空格的文件名
- 21---包管理(rpm & yum)
- 使用JAVA Get POST 抓网页的练习代码