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 }

你可能感兴趣的:(unique)