POJ 1679题
//题目大意:判断最小生成树是否唯一
//解题思路:求出最小的次小生成树,判断最小的次小生成树是否与最小生成树的权值和一样,如果一样,则唯一,否则不唯一
//求次小生成树的算法:
(1)有个比较直观的方法。依次把MST上的边删掉,然后再做MST。这样的做就是编码简洁,有模块可以重用。效率O(n*O(MST))
(2)既然每替换出去一条边,都将使代价变大。所以求次小生成树,只需要替换出去一条边即可。枚举不在树上的边eij,树上路径上的边权都不大于w[i][j]。
选取其中最大的与eij替换,得到新树的权重。选取替换后最小的一个就是次小生成树了。这个过程可以做到O(m).然后就是计算出path_max[i][j]表示i,j树上路径中的最大边权。
在计算MST的时候,点是依次加入到树中。当前加入的是j,与他相连的是i。那么,对所有在树上集合中的k:path_max[k][j] = max{ path_max[k][i] , w[i][j] }
因为path_max[][]的每个元素都要计算,且仅一次。累加起来的复杂度是O(n^2).总的复杂度:O(MST)+O(n^2)+O(m)
最小生成树的详细解释见:http://hi.baidu.com/hplonline/blog/item/56fab312cc7677c7c2fd7819.html
#include <stdio.h>
#include <algorithm>
using namespace std;
#define arraysize 101
typedef struct node
{
int v;
int u;
int w;
}node;
node edges[arraysize*arraysize];
int r[arraysize];
int parent[arraysize];
int n,m;
int flag[arraysize]; //记录最小生成树中使用到的边
int maxdata = 0x7fffffff;
bool cmp(node a,node b)
{
return a.w<b.w;
}
int findroot(int n)
{
if(parent[n] == n)
return n;
else
parent[n] = findroot(parent[n]);
return parent[n];
}
void init()
{
for(int i3=1;i3<n+1;++i3)
{
parent[i3] = i3;
r[i3] = 1;
}
}
int main()
{
//freopen("1.txt","r",stdin);
int t;
while(scanf("%d",&t)!=EOF)
{
for(int i=0;i<t;++i)
{
//求最小生成树
int sum = 0; //记录最小生成树的权值和
scanf("%d%d",&n,&m);
int start,end,weight;
for(int i1=1;i1<m+1;++i1)
{
scanf("%d%d%d",&start,&end,&weight);
edges[i1].v = start;
edges[i1].u = end;
edges[i1].w = weight;
}
sort(edges+1,edges+m+1,cmp); //此处排序时注意排序的范围
init();
int p=0;
for(int i2=1;i2<m+1;++i2) //遍历生成树中的所有边
{
int a = edges[i2].v;
int b = edges[i2].u;
int roota = findroot(a);
int rootb = findroot(b);
if(roota!=rootb)
{
p++;
flag[p] = i2; //技巧:记录最小生成树中使用到了的边
if(r[roota]>=r[rootb])
{
parent[rootb] = roota;
r[roota] += r[rootb];
}
else
{
parent[roota] = rootb;
r[rootb] += r[roota];
}
sum += edges[i2].w;
}
if(p==n-1) //最小生成树生成完毕
break;
}
int mincisum = maxdata; //记录次小生成树中的最小树的权值和
int cisum = 0; //记录次小生成树的权值和
//依次删除最小生成树中的边,从图中剩下的所有边中再求最小生成树
for(int i4=1;i4<n;++i4)
{
int edgenum = 0;
init();
cisum = 0;
for(int i5=1;i5<m+1;++i5) //遍历所有的边
{
if(flag[i4] == i5) //删除某一条边
continue;
int a = edges[i5].v;
int b = edges[i5].u;
int roota = findroot(a);
int rootb = findroot(b);
if(roota!=rootb)
{
edgenum++;
if(r[roota]>=r[rootb])
{
parent[rootb] = roota;
r[roota] += r[rootb];
}
else
{
parent[roota] = rootb;
r[rootb] += r[roota];
}
cisum += edges[i5].w;
}
if(edgenum == n-1) //次小生成树生成完毕
break;
}
if(edgenum == n-1 && cisum < mincisum) //求所有次小生成树中的最小树
mincisum = cisum;
}
if(mincisum !=sum) //判断次小生成树中的最小树是否与最小生成树的权值和相等
{
printf("%d\n",sum);
}
else
printf("Not Unique!\n");
}
}
return 0;
}