次小生成树(POJ1679/CDOJ1959)

POJ1679

首先求出最小生成树,记录权值之和为MinST。然后枚举添加边(u,v),加上后必形成一个环,找到环上非(u,v)边的权值最大的边,把它删除,计算当前生成树的权值之和,取所有枚举加边后生成树权值之和的最小值

思路: 

最小生成树唯一性判断,求次小生成树的val再与最小生成树比较。
1.先用prim算法求出最小生成树。并在其过程中保存加入到MST中的Max[i][j]( i 到 j 路径中的最大边 ) 
2.对未加入MST的边进行遍历:从MST中去掉i j路径中的最大边,加入未访问边G[i][j],如果得到的生成树的权值和MST的相等,则存在次小生成树的权值=MST的权值和

 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <cstring>
 6 using namespace std;
 7 #define INF 0x3f3f3f3f
 8 
 9 const int N = 1e3+6;
10 int G[N][N]; // graph matrix
11 int f[N];    // pre node
12 int vis[N];  // visited node
13 int dis[N];  // main matrix: the edges(set-u)
14 int mark[N][N];
15 int Max[N][N]; // path from i to j max edge
16 int n,m,best;
17 
18 int prim(int v)
19 {
20     int ans=0;
21     vis[v]=1;
22     for(int i=1;i<=n;i++)
23     {
24         dis[i]=G[v][i];
25         f[i]=v;
26     }
27     dis[v]=0;
28     for(int i=1;i<n;i++) // n-1
29     {
30         int u=-1;
31         for(int j=1;j<=n;j++)
32             if(!vis[j]&&(u==-1||dis[j]<dis[u]))u=j;
33         if(u==-1)break;
34         vis[u]=1;
35         mark[u][f[u]] = mark[f[u]][u] = 1;
36         ans += dis[u];
37         for(int j=1;j<=n;j++)
38         {
39             // compare with visited node record path max edge
40             if(vis[j])Max[u][j]=Max[j][u]=max(Max[j][f[u]],dis[u]);
41             if(!vis[j]&&dis[j]>G[u][j])
42             {
43                 dis[j]=G[u][j];
44                 f[j]=u;
45             }
46         }
47     }
48     return ans;
49 }
50 
51 int SMST()
52 {
53     int mini=INF;
54     for(int i=1;i<=n;i++)
55         for(int j=1;j<=n;j++)  //or for(int j=i+1;j<=n;j++)
56         {
57             if(i!=j&&!mark[i][j])
58                 mini = min(mini,best+G[i][j]-Max[i][j]);
59         }
60     return mini;
61 }
62 
63 int main()
64 {
65     int x,y,w,T;
66     scanf("%d",&T);
67     while(T--)
68     {
69         memset( vis, 0 ,sizeof(vis) );
70         memset( Max, 0 ,sizeof(Max) );
71         memset( f, 0 ,sizeof(f) );
72         memset( dis, 0 ,sizeof(dis) );
73         memset( mark, 0 ,sizeof(mark) );
74         scanf("%d%d",&n,&m);
75         for(int i=1;i<=n;i++)
76             for(int j=1;j<=n;j++)
77             {
78                 if(i==j)G[i][j]==0;
79                 else G[i][j]=INF;
80             }
81         for(int i=0;i<m;i++)
82         {
83             scanf("%d%d%d",&x,&y,&w);
84             G[x][y]=w;
85             G[y][x]=w;
86         }
87         best = prim(1);
88         int temp = SMST();
89         if(temp == best)
90             printf("Not Unique!
");
91         else
92             printf("%d
",best);
93     }
94     return 0;
95 }

CDOJ 1959

思路:这道题会出现重边,用Kruskal算法处理起来比较方便。(用邻接表方便,邻接矩阵还要考虑同两点之间的多条权值相同的边。)

次小生成树的权值如果等于最小生成树的权值, 其替换边只会是权值相同的边,于是可以把权值相同边放在一起考虑,分别判断全部没有合并时有多少可以合并(cnt1记录的是可以加入集合的边数),边合并边判断有多少可以合并(cnt2记录的是选中一条加入到集合),如果可选的大于构成最小生成树所需要的,那么就存在一种相同的边权可以替换原来的,从而最小生成树不唯一。

 1 #include <bits/stdc++.h>
 2 #define LL long long
 3 using namespace std;
 4 const int N = 2e5+6;
 5 struct Node
 6 {
 7     int u,v;
 8     LL w;
 9 }G[N];
10 
11 int n,m;
12 int f[2002];
13 int find(int x)
14 {
15     return x==f[x]?x:f[x]=find(f[x]);
16 }
17 
18 bool cmp(Node & a, Node & b)
19 {
20     return a.w<b.w;
21 }
22 
23 void kruskal()
24 {
25     int cnt1=0;
26     int cnt2=0;
27     for(int i=1;i<=n;i++)f[i]=i;
28     sort(G,G+m,cmp);
29     for(int i=0;i<m;)
30     {
31         int j=i;
32         while(j<m&&G[j].w==G[i].w)
33         {
34             int u = find(G[j].u);
35             int v = find(G[j].v);
36             if(u!=v)cnt1++;
37             j++;
38         }
39         j=i;
40         while(j<m&&G[j].w==G[i].w)
41         {
42             int u = find(G[j].u);
43             int v = find(G[j].v);
44             if(u!=v){
45                 cnt2++;
46                 f[u]=v;
47             }
48             j++;
49         }
50         i=j;
51         if(cnt1>cnt2)break;
52     }
53     if( cnt1 > cnt2 ){
54         printf("zin
");
55     }else{
56         printf("ogisosetsuna
");
57     }
58 }
59 
60 int main()
61 {
62     int x,y;
63     LL w;
64     cin>>n>>m;
65     for(int i=0;i<m;i++)
66     {
67         cin>>x>>y>>w;
68         G[i].u=x;
69         G[i].v=y;
70         G[i].w=w;
71     }
72     kruskal();
73     return 0;
74 }
原文地址:https://www.cnblogs.com/demian/p/9188918.html