POJ 1679 The Unique MST 次小生成树
2012-08-19 19:25 javaspring 阅读(219) 评论(0) 收藏 举报来源:http://poj.org/problem?id=1679
题意:判断最小生成树是否唯一。
思路:求出最小生成树后再求次小生成树,如果次小生成树的长度和最小生成树的长度相等,则最小生成树不唯一,否则最小生成树唯一。
介绍一下我求次小生成树的方法。
次小生成树可以用kruskal和prime两种方法求。kruskal算法求的过程就是多次求最小生成树。我们第一次求最小生成树的时候把边的序号都标记下来,然后以后多次求最小生成树的时候,每次都不用第一次求得最小生成树的时候的边。比如说,第一次求最小生成树用到了边1 6 7,则我们再求3次最小生成树,第一次不用边1,第二次不用边6,第三次不用边7.。这样求三次,若中间有哪一次和第一次的的和相等,则说明不唯一。否则,唯一。这道题的话,倘若用kruskal算法的话,复杂度是2亿,会超。这道题可以用prime算法。
用prime算法求次小生成树的过程是先求一遍最小生成树,在求得过程中我们设置一个pre数组,表示的是某个点的前驱,比如说刚开始选到点1,然后选到的第一条边是从1到3的,则pre[3] = 1,并且在求最小生成树的过程中把选到的边记录下来。我们再设置另一个数组maxedge,表示在构造好的树中,如果x和y之间有一条边,且该边没有在最小生成树中,则maxedge[x][y] 表示加上这条边后,所能去掉的最大的边。比如说,我们求出一颗最小生成树后,此时x和y有一条边,且这条边没有被用到,此时maxedge[x][y]表示生成树中从x到y的路径中的最大的一条边,也就是说连接x和y的路径中能够去掉的最大的边。也就是说求出最小生成树后,在遍历图中所有的没有用到的边求次小生成树即可。如果某个值和最小生成树的值相同,则最小生成树不唯一。否则最小生成树唯一。
代码:
//1679 #include <iostream> #include <cstdio> #include <climits> #include <string.h> using namespace std; #define CLR(arr,val) memset(arr,val,sizeof(arr)) const int N = 110; int vis[N],map[N][N],numpoint,numroad,use[N][N],maxedge[N][N]; int len1,len2,pre[N]; int max(int a,int b){ return a > b ? a:b; } int min(int a,int b){ return a < b ? a:b; } void prime(){ int dis[N],temp,pos = 0; CLR(vis,0); CLR(maxedge,0); for(int i = 1; i <= numpoint; ++i){ dis[i] = map[1][i]; pre[i] = 1; } dis[1]= 0; vis[1] = 1; len1 = 0; for(int i = 1; i <= numpoint; ++i){ temp = INT_MAX; for(int j = 1; j <= numpoint; ++j){ if(temp > dis[j] && !vis[j]){ temp = dis[j]; pos = j; } } if(temp == INT_MAX) break; vis[pos] = 1; pre[pos] = i; use[i][pos] = use[pos][i] = 0; len1 += temp; for(int j = 1; j <= numpoint; ++j){ if(vis[j]) maxedge[j][pos] = maxedge[pos][j] = max(maxedge[pre[pos]][j],map[pre[pos]][pos]); if(!vis[j]){ if(dis[j] > map[pos][j]){ dis[j] = map[pos][j]; pre[j] = pos; } } } } } void second_prime(){ len2 = INT_MAX; for(int i = 1; i <= numpoint; ++i){ for(int j = 1; j <= numpoint; ++j){ if(use[i][j]){ len2 = min(len2,len1 + map[i][j] - maxedge[i][j]); } } } } int main(){ //freopen("1.txt","r",stdin); int numcase; scanf("%d",&numcase); while(numcase--){ for(int i = 0; i < N; ++i) for(int j = 0; j < N; ++j) map[i][j] = INT_MAX; CLR(use,0); CLR(maxedge,0); CLR(pre,0); scanf("%d%d",&numpoint,&numroad); int x,y,z; for(int i = 0; i < numroad; ++i){ scanf("%d%d%d",&x,&y,&z); map[x][y] = map[y][x] = z; use[x][y] = use[y][x] = 1; } prime(); second_prime(); if(len1 == len2) printf("Not Unique!\n"); else{ printf("%d\n",len1); } } return 0; }