HDU1102 Constructing Roads —— 最小生成树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1102
题解:
纯最小生成树,只是有些边已经确定了要加入生成树中,特殊处理一下这些边就可以了。
kruskal算法:
由于有些边已经确定,所以在调用kruskal()之前,就把这条边的两个顶点放在一个集合就可以了。
1 #include<cstdio>//hdu1102 最小生成树 kruskal 2 #include<algorithm> 3 #define N 110 4 using namespace std; 5 6 struct node 7 { 8 int x,y,dis; 9 }edge[N*N]; 10 11 int fa[N]; 12 13 bool cmp(node a, node b) 14 { 15 return a.dis<=b.dis; 16 } 17 18 int find(int x) 19 { 20 return fa[x]==x?x:find(fa[x]); 21 } 22 23 bool un(int x, int y) 24 { 25 x = find(x); 26 y = find(y); 27 if(x!=y) 28 { 29 fa[x] = y; 30 return true; 31 } 32 return false; 33 } 34 35 int kruskal(int n,int sum) 36 { 37 int len = 0,x,y,i; 38 sort(edge,edge+sum,cmp); 39 for(i = 0; i<sum; i++) 40 { 41 x = edge[i].x; 42 y = edge[i].y; 43 if(un(x,y)) 44 len += edge[i].dis; 45 } 46 return len; 47 } 48 49 int main() 50 { 51 int n,m,i,j,x,y,dis,sum; 52 while(scanf("%d",&n)!=EOF) 53 { 54 sum = 0; 55 for(i = 1; i<=n; i++) 56 for(j = 1; j<=n; j++) 57 { 58 scanf("%d",&dis); 59 if(i>=j) continue; 60 edge[sum].x = i; 61 edge[sum].y = j; 62 edge[sum].dis = dis; 63 sum++; 64 } 65 66 for(i = 1; i<=n; i++) 67 fa[i] = i; 68 69 scanf("%d",&m); 70 for(i = 0; i<m; i++)//先处理以确定的边 71 { 72 scanf("%d%d",&x,&y); 73 un(x,y); 74 } 75 printf("%d\n",kruskal(n,sum)); 76 } 77 return 0; 78 }
prim算法:
在prim算法中就不能像kruskal算法那样先预处理了,而要在算法中运行。由于prim算法在每次松弛时总是找最小的点,题目中两条村庄的距离是非负数,那么我们可以把确定要加入生成树的边的两个顶点的距离设为-1,这样就能确保他们都加入到生成树中了。
注意:赋值或修改邻接矩阵的无向图时, 正反两条边都要操作。
1 #include<cstdio>//hdu1102 最小生成树 prim 2 #include<algorithm> 3 #define N 110 4 #define INF 0x7fffffff 5 using namespace std; 6 7 int arc[N][N]; 8 9 int prim(int n) 10 { 11 int low[N]; 12 int k,i,j,min,len = 0; 13 for(i = 1; i<=n; i++) 14 { 15 low[i] = arc[1][i]; 16 } 17 18 for(i = 2; i<=n; i++) 19 { 20 min = INF; 21 for(j = 2; j<=n; j++) 22 { 23 if(low[j]!=0 && low[j]<min) 24 { 25 min = low[j]; 26 k = j; 27 } 28 } 29 30 if(low[k]>0) 31 len += low[k]; 32 low[k] = 0; 33 34 for(int j = 2; j<=n; j++) 35 { 36 if(low[j]!=0 && arc[k][j]<low[j]) 37 { 38 low[j] = arc[k][j]; 39 } 40 } 41 } 42 return len; 43 } 44 45 int main() 46 { 47 int n,q,u,v,i,j; 48 while(scanf("%d",&n)!=EOF) 49 { 50 for(i = 1; i<=n; i++) 51 for(j = 1; j<=n; j++) 52 scanf("%d",&arc[i][j]); 53 54 scanf("%d",&q); 55 for(i = 0; i<q; i++) 56 { 57 scanf("%d%d",&u,&v); 58 arc[v][u] = arc[u][v] = -1; 59 //正反向都要置为-1, 因为在松弛的过程中,两个方向都有可能 60 } 61 printf("%d\n",prim(n)); 62 } 63 return 0; 64 }