POJ的题目地址:http://poj.org/problem?id=1679
次小生成树是给定一个无向图,让我们计算得出这个无向图的最小生成树是否是唯一的。对于一个无向图的最小生成树,我们知道,它是连通的,而且每一条边都为割边。所以,当我们拿去一条边L的时候,树就会分裂成两个联通分支。这时,我们加入一条异于L的边L1,令两个联通分支重新形成一棵树。如果L1的长度和L的长度相等。那么我们就可以得到两棵最小生成树。此时即存在次小生成树。
在这里,我们利用kruskal算法,在kruskal算法中,每一次加入一条边,即将两个联通分支联通起来。而每个联通分支即可视为一棵小树。而小树存在一个树根。如果在加入一条边的之后能够找到同样长度的一条边,让两个联通分支连接起来(即这条边的两个节点可以回溯到同样的两个树根),那么我们就可以知道这棵树存在次小生成树。问题就得到了解决。
同样的,用于求最小生成树的PRIM算法也可以用来求次小生成树。
下面附上题目的源代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct road
{
int a, b, v;
}data[25100];
int father[105], dev[105],n,m,t;
bool comp(road a, road b)
{
if (a.v < b.v)return true;
else return false;
}
void init()
{
int i;
for (i = 1; i <= n; i++)
{
father[i] = i;
dev[i] = 0;
}
}
int findroot(int a)
{
if (a != father[a])
father[a] = findroot(father[a]);
return father[a];
}
void union_set(int a, int b)
{
if (father[a] == father[b])return;
else if (dev[a] >= dev[b])
{
dev[a]++;
father[b] = a;
}
else
{
dev[b]++;
father[a] = b;
}
}
int kruskal()
{
int sum=0, flag = 0,i,x,y,j;
sort(data + 1, data + m+1, comp);
for (i = 1; i <= m; i++)
{
if (findroot(data[i].a) == findroot(data[i].b))continue;
else
{
x = findroot(data[i].a); y = findroot(data[i].b);
for (j = i + 1; j <= m;j++)
{
if (data[j].v != data[i].v)break;
else if (findroot(data[j].a) == x&&findroot(data[j].b) == y)
{
flag = 1;
break;
}
}
}
if (flag)break;
union_set(data[i].a, data[i].b);
sum += data[i].v;
}
if (flag)return -1;
else return sum;
}
int main()
{
int i, a,b,ans;
cin >> t;
while (t--)
{
cin >> n >> m;
init();
for (i = 1; i <= m; i++)
{
cin >> a >> b >> data[i].v;
data[i].a = min(a, b);
data[i].b = max(a, b);
}
ans = kruskal();
if (ans < 0)cout << "Not Unique!" << endl;
else cout << ans << endl;
}
return 0;
}