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 }
posted @ 2012-10-03 15:35  Naix_x  阅读(194)  评论(0编辑  收藏  举报