具体操作是:
step 1. 在T_i中任取一条不在T中的边u_v.[次小生成树]
类比上述次短路径求法,很容易想到一个“枚举删除最小生成树上的每条边,再求最小生成树”的直观解法。如果用Prim+堆,每次最小生成树时间复杂度为O(N*log(N+M) + M),枚举删除有O(N)条边,时间复杂度就是O(N^2*log(N+M) + N*M),当图很稠密时,接近O(N^3)。这种方法简易直观,但我们有一个更简单,而且效率更高的O(N^2+M)的解法,下面介绍这种方法。
首先求出原图最小生成树,记录权值之和为MinST。枚举添加每条不在最小生成树上的边(u,v),加上以后一定会形成一个环。找到环上权值第二大的边(即除了(u,v)以外的权值最大的边),把它删掉,计算当前生成树的权值之和。取所有枚举修改的生成树权值之和的最小值,就是次小生成树。
具体实现时,更简单的方法是从每个节点i遍历整个最小生成树,定义F[j]为从i到j的路径上最大边的权值。遍历图求出F[j]的值,然后对于添加每条不在最小生成树中的边(i,j),新的生成树权值之和就是MinST + w(i,j) – F[j],记录其最小值,则为次小生成树。
该算法的时间复杂度为O(N^2 + M)。由于只用求一次最小生成树,可以用最简单的Prim,时间复杂度为O(N^2)。算法的瓶颈不在求最小生成树,而在O(N^2+M)的枚举加边修改,所以用更好的最小生成树算法是没有必要的。
pku 1679
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=106;
int map[maxn][maxn];
int v[maxn];
int dis[maxn];
vector<int>q[maxn];
int nNode,nEdge; //点数,边数
struct node
{
int s,t,cap;
}e[10002];
int fa[2002];
bool cmp(node a,node b)
{
return a.cap>b.cap;
}
int getfa(int x)
{
if(fa[x]!=x)
fa[x]=getfa(fa[x]);
return fa[x];
}
int kruskal()
{
make_heap(e,e+nEdge,cmp);
int num=0;
int x,y,ans;
ans=0;
while(nEdge>0)
{
pop_heap(e,e+nEdge,cmp);
nEdge--;
x=getfa(e[nEdge].s);
y=getfa(e[nEdge].t);
if(x!=y)
{
fa[x]=y;
q[e[nEdge].s].push_back(e[nEdge].t);
q[e[nEdge].t].push_back(e[nEdge].s);
ans+=e[nEdge].cap;
num++;
}
if(num==nNode-1)
break;
}
return ans;
}
void dfs(int fa,int u) //参数为要搜的节点y与它的父节点x,搜节点到根的最大边
{
dis[u]=dis[fa]>map[fa][u]?dis[fa]:map[fa][u];
int i;
for(i=0;i<q[u].size();i++)
{
if(q[u][i]!=fa)
dfs(u,q[u][i]);
}
}
int main()
{
int nca,a,b,c;
scanf("%d",&nca);
while(nca--)
{
scanf("%d%d",&nNode,&nEdge);
int i,j;
for(i=0;i<=nNode;i++)
{
while(!q[i].empty())
q[i].clear();
for(j=0;j<=nNode;j++)
map[i][j]=INT_MAX;
}
for(i=0;i<nEdge;i++)
{
scanf("%d%d%d",&a,&b,&c);
e[i].s=a;
e[i].t=b;
e[i].cap=c;
map[a][b]=c<map[a][b]?c:map[a][b];
map[b][a]=map[a][b];
}
for(i=0;i<=nNode;i++)
fa[i]=i;
int ans=kruskal();
int res=INT_MAX;
for(i=1;i<=nNode;i++)
{
memset(v,0,sizeof(v));
memset(dis,0,sizeof(dis));
v[i]=1;
for(j=0;j<q[i].size();j++)
{
v[q[i][j]]=1;
dfs(i,q[i][j]);
}
for(j=1;j<=nNode;j++)
{
if(!v[j])
{
if((map[i][j]!=INT_MAX)&&((ans+map[i][j]-dis[j])<res))
{
res=ans+map[i][j]-dis[j];
}
}
}
if(ans==res)
break;
}
if(ans!=res)
printf("%d\n",ans);
else
printf("Not Unique!\n");
}
return 0;
}