最小生成树(prim和Kruskal)
http://poj.org/problem?id=1751
题意:给出n个城镇(编号1-N)的坐标,m条已经建好的边(无向)。问需要在哪些城镇之间建边,使得n个城镇联通,且代价(距离)最小,输出所以需要在两城镇间建边的编号。
解法:kruskal
注意:在已经建好的边中不一定就是最小生成树中所需要的边,所以也需要并查集筛选。
记录选好边的数量,n-1条边即可。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <algorithm> #include <iostream> #include<cstdio> #include<string> #include<cstring> #include <stdio.h> #include <queue> #include <string.h> #include <vector> #include <map> #define ME(x , y) memset(x , y , sizeof(x)) #define SF(n) scanf("%d" , &n) #define rep(i , n) for(int i = 0 ; i < n ; i ++) #define INF 0x3f3f3f3f #define mod 1024 using namespace std; typedef long long ll ; int n , m ; int ans ; int fa[809]; struct node { int x , y ; }a[809]; struct node1{ int from , to ; double w ; }ma[300009]; void init() { memset(vis , 0 , sizeof(vis)); ans = 0 ; for(int i = 1 ; i <= n ; i++) fa[i] = i ; } int find(int x) { return x == fa[x] ? x : find(fa[x]); } void unite(int u , int v) { u = find(u) , v = find(v); if(u > v) fa[u] = v ; else fa[v] = u ; } bool cmp(node1 a , node1 b) { return a.w < b.w ; } int main() { scanf("%d" , &n); init(); for(int i = 1 ; i <= n ; i++) { scanf("%d%d" , &a[i].x , &a[i].y); } //连通网 int way = 0 ; for(int i = 1 ; i <= n ; i++) { for(int j = i + 1 ; j <= n ; j++) { double w ; w = sqrt(pow(a[i].x - a[j].x , 2) + pow(a[i].y - a[j].y , 2)); ma[way].from = i , ma[way].to = j , ma[way].w = w ; way ++ ; } } scanf("%d" , &m); for(int i = 1 ; i <= m ; i++) { int u , v ; scanf("%d%d" , &u , &v); if(find(fa[u]) != find(fa[v])) { unite(u , v); ans++ ; } } sort(ma , ma + way , cmp); for(int j = 0 ; j < way ; j++) { if(find(fa[ma[j].from]) != find(fa[ma[j].to])) { unite(ma[j].from , ma[j].to); printf("%d %d\n" , ma[j].from , ma[j].to); ans++; } if(ans == n - 1) break ; } return 0 ; }
prim:有几个比较妙的处理:1、将已存在的边权赋值为0,2、新开一个p数组记录每条最优边。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <algorithm> #include <iostream> #include<cstdio> #include<string> #include<cstring> #include <stdio.h> #include <queue> #include <string.h> #include <vector> #include <map> #define ME(x , y) memset(x , y , sizeof(x)) #define SF(n) scanf("%d" , &n) #define rep(i , n) for(int i = 0 ; i < n ; i ++) #define INF 0x3f3f3f3f #define mod 1024 using namespace std; typedef long long ll ; double ma[809][809]; int n , m ; int vis[809]; double dis[809]; int p[809]; int ans ; struct node { int x , y ; }a[809]; void init() { for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) if(i == j) ma[i][i] = 0; else ma[i][j] = INF; memset(vis , 0 , sizeof(vis)); ans = 0 ; } void Dijia() { for(int i = 1 ; i <= n ; i++) { dis[i] = ma[1][i]; p[i] = 1 ;//初始为所有城镇与1连接为最优,与dis数组相似 } vis[1] = 1 ; for(int i = 1 ; i < n ; i++) { double min1 = INF ; int pos ; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && dis[j] < min1) { min1 = dis[j]; pos = j ; } } vis[pos] = 1 ; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && dis[j] > ma[pos][j]) { dis[j] = ma[pos][j]; p[j] = pos ;//当有更优的路线到j城镇更新距离,更新与j城镇相连的城镇号 } } } } int main() { scanf("%d" , &n); init(); for(int i = 1 ; i <= n ; i++) { scanf("%d%d" , &a[i].x , &a[i].y); } //连通网 for(int i = 1 ; i <= n ; i++) { for(int j = i + 1 ; j <= n ; j++) { double w ; w = sqrt(pow(a[i].x - a[j].x , 2) + pow(a[i].y - a[j].y , 2)); ma[j][i] = ma[i][j] = min(ma[i][j] , w); } } scanf("%d" , &m); for(int i = 1 ; i <= m ; i++) { int u , v ; scanf("%d%d" , &u , &v); ma[u][v] = ma[v][u] = 0 ;//将存在的道路间的距离赋值为0 } Dijia(); for(int i = 1 ; i <= n ; i++) { if(ma[i][p[i]] != 0) printf("%d %d\n" , i , p[i]); } return 0 ; }