POJ 1679 The Unique MST(次小生成树)
题意:最小生成树是否唯一。
数据比较水,用最朴实的方法过的,O(n^2+e)再学一学,这个朴实方法改了N处,,思路简单,Kruskal写的,就是删除生成树里的每一条边,求最小生成树,唉,这是写的啥啊。。。。
1 #include <cstdio> 2 #include <cstring> 3 #include <map> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 int o[101],num,sum,m,n; 8 struct edge 9 { 10 int sv; 11 int ev; 12 int w; 13 int flag; 14 } p[200001]; 15 int cmp(const edge &a,const edge &b) 16 { 17 if(a.w < b.w) 18 return 1; 19 else 20 return 0; 21 } 22 int find(int x) 23 { 24 while(x != o[x]) 25 x = o[x]; 26 return x; 27 } 28 void merge(int id,int x,int y,int w,int z) 29 { 30 x = find(x); 31 y = find(y); 32 if(x != y) 33 { 34 sum += w; 35 o[x] = y; 36 if(z == -1)//第一次寻找最小生成树的时候记录下来 37 p[id].flag = 1; 38 num ++; 39 } 40 } 41 int kruskal(int x) 42 { 43 int i; 44 num = 0; 45 sum = 0; 46 for(i = 1; i <= n; i ++) 47 o[i] = i; 48 for(i = 1; i <= m; i ++) 49 { 50 if(i == x) continue;//删去边x的时候寻找最小生成树 51 if(num == n-1) break; 52 merge(i,p[i].sv,p[i].ev,p[i].w,x); 53 } 54 if(num == n-1)//特判是否存在最小生成树 55 return sum; 56 else 57 return -1; 58 } 59 int main() 60 { 61 int i,ans,t; 62 scanf("%d",&t); 63 while(t--) 64 { 65 scanf("%d%d",&n,&m); 66 for(i = 1; i <= m; i ++) 67 { 68 scanf("%d%d%d",&p[i].sv,&p[i].ev,&p[i].w); 69 p[i].flag = 0; 70 } 71 sort(p+1,p+m+1,cmp); 72 ans = kruskal(-1);//求最小生成树 73 int z = 1,temp; 74 for(i = 1; i <= m&&z; i ++) 75 { 76 if(p[i].flag == 1)//删除生成树中的边 77 { 78 temp = kruskal(i);//求次小生成树 79 if(ans == temp) 80 z = 0; 81 } 82 } 83 if(z) 84 printf("%d\n",ans); 85 else 86 printf("Not Unique!\n"); 87 } 88 return 0; 89 }
O(n^2+e)做法就是看了一下有的kruskal先求出整个生成树,然后先用BFS预处理出生成树上,每一个点和另外的点之间最大的长度,Max[x][y],然后枚举不在生成树的边,min(ans-Max[x][y]+w(x,y))就是次小生成树了。感觉用kruskal比较麻烦,用prim写就比较方便了,可以一边处理,一边更新Max[x][y]。下边代码用prim写的,其主要的思想就是存起他的父亲节点,用生成树里的点到父亲的最大距离,与父亲到自己的距离比较,取大。
1 #include <cstdio> 2 #include <cstring> 3 #include <map> 4 #include <cmath> 5 #include <algorithm> 6 #define N 100000000 7 using namespace std; 8 int p[201][201],low[201],o[201],dis[201][201],que[201],fath[201],key[201][201]; 9 //dis数组存节点x到y的最大距离,que队列存进入生成树的节点 10 //fath存节点的父亲节点,key标记这条边是否在生成树上。 11 int Max(int a,int b) 12 { 13 return a > b ? a : b; 14 } 15 int main() 16 { 17 int i,j,k,n,m,t,sv,ev,w,ans,minz,temp; 18 scanf("%d",&t); 19 while(t--) 20 { 21 memset(p,0,sizeof(p)); 22 memset(o,0,sizeof(o)); 23 memset(dis,0,sizeof(dis)); 24 memset(key,0,sizeof(key)); 25 scanf("%d%d",&n,&m); 26 for(i = 1; i <= n; i ++) 27 { 28 for(j = 1; j <= n; j ++) 29 p[i][j] = N; 30 } 31 for(i = 1; i <= m; i ++) 32 { 33 scanf("%d%d%d",&sv,&ev,&w); 34 if(p[sv][ev] > w) 35 { 36 p[sv][ev] = w; 37 p[ev][sv] = w; 38 } 39 } 40 for(i = 1; i <= n; i ++) 41 { 42 low[i] = p[1][i]; 43 } 44 o[1] = 1; 45 ans = 0; 46 int top; 47 top = 1; 48 que[1] = 1; 49 for(i = 1;i <= n;i ++) 50 { 51 fath[i] = 1; 52 } 53 for(i = 1; i <= n-1; i ++) 54 { 55 minz = N; 56 for(j = 1; j <= n; j ++) 57 { 58 if(minz > low[j]&&!o[j]) 59 { 60 minz = low[j]; 61 k = j; 62 } 63 } 64 key[fath[k]][k] = 1; 65 key[k][fath[k]] = 1; 66 if(minz == N) break; 67 o[k] = 1; 68 ans += minz; 69 for(j = 1; j <= top; j ++) //更新队列中的点到新加入的k的边上最大边,也就是dis数组 70 { 71 dis[que[j]][k] = dis[k][que[j]] = Max(minz,dis[que[j]][fath[k]]); 72 } 73 que[++top] = k;//k节点加入队列 74 for(j = 1; j <= n; j ++) 75 { 76 if(p[k][j] < low[j]&&!o[j]) 77 { 78 low[j] = p[k][j]; 79 fath[j] = k;//记录父亲 80 } 81 } 82 } 83 temp = N; 84 for(i = 1; i <= n; i ++) 85 { 86 for(j = 1; j <= n; j ++) 87 { 88 if(i != j&&p[i][j] != N&&!key[i][j])//枚举不在生成树的每一条边 89 { 90 if(temp > ans-dis[i][j]+p[i][j]) 91 temp = ans-dis[i][j]+p[i][j]; 92 } 93 } 94 } 95 if(temp == ans) 96 printf("Not Unique!\n"); 97 else 98 printf("%d\n",ans); 99 } 100 return 0; 101 }