次小生成树 poj1679
遇到一道需要思考的题,所以把他写下来,见代码注释
#include<stdio.h>
#include<iostream>
#include<queue>//什么是最小生成树?简单说就是第二小的树,这个第二小不一定是总权值第二小,也可能和MST一样的
#include<stack>//题目:判断最小生成树是否唯一
#include<vector> //开始的时候 用的另一种枚举但是有个细节没处理掉 就错了
#include<string.h>//最小生成树 运用的一个重要结论就是
#include<math.h> //次小生成树就是最小生成树的一条边替换成另一条不在最小生成树的边 也就是只换一条就行
#include<algorithm>//而且其他的边不变;这个结论不是很懂 给不出证明
#define inf 0x3f3f3f3f
const double INF=0x3f3f3f3f;
const int max_n=105;
using namespace std;
int n,m;
int dist[max_n],maps[max_n][max_n],past[max_n],maxb[max_n][max_n];
bool bian[max_n][max_n];
void init()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
maps[i][j]=inf;//其他的设为无穷大
}
int prim(int src)
{
int u;int ans=0;
bool ins[max_n];
memset(ins,false,sizeof(ins));//一定要赋初始值 记住啊 错了好几回了
memset(bian,false,sizeof(bian));//标记边是否在MST中
memset(maxb,-1,sizeof(maxb));//记录i到j的最大边
for(int i=1;i<=n;i++){
dist[i]=maps[src][i];//初始原点到其他点的距离
if(i!=src)
past[i]=src;
}
ins[src]=1;past[src]=-1;
for(int i=1;i<n;i++)//1~n直接就是算全部 原点是零 所以这里能选出来 如果出现的负值边的话 就不能了
{
int mindst=inf;u=-1;
for(int i=1;i<=n;i++)
if(!ins[i] && dist[i]<mindst)
mindst=dist[i],u=i;
if(u==-1) return -1;//这个就是不联通
ans+=mindst;//加权边
ins[u]=1;//加入
bian[u][past[u]]=bian[past[u]][u]=true;
for(int i=1;i<=n;i++)
{
if(ins[i])maxb[u][i]=maxb[i][u]=max(maxb[i][past[u]],dist[u]);//更新i到u的最大边 就是 i到past[u] 与dist[u]看哪个大
if(!ins[i]&&dist[i]>maps[u][i])//dijkstra与prim不同之处就在于更新的这个地方
{
dist[i]=maps[u][i];//dijkstra更新是更新点到整个树的距离 而prim是更新节点之间的距离
past[i]=u;
}
}
}
return ans;
}
int smst(int ans)
{
int Min=inf;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
{
if(!bian[i][j] && maps[i][j]!=inf)
Min=min(Min,ans-maxb[i][j]+maps[i][j]);
}
return Min;
}
int main()
{
int t,flag=0,x,y,data;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&x,&y,&data);
maps[x][y]=maps[y][x]=data;
}
int ans=prim(1);
if(ans==-1)
printf("Not Unique!\n");
else
{
if(ans==smst(ans))//次小生成树也可能等于最小生成树
printf("Not Unique!\n");
else
printf("%d\n",ans);
}
}
return 0;
}
总结下 ,其实求次小生成树,利用那个重要结论:次小生成树/就是最小生成树中的一条边/替换成/另一条不属于最小生成树的边。也就是只换一条就行了,而且MST的其他的边不变;这个结论不是很懂 给不出证明;
那么怎么替换那条边?也就是遍历每一条不属于MST(最小生成树)的边,寻找最小的;
既然说是替换,那么就要删除MST的一条边,然后换成不属于MST的边;这样可能不理解
换句话说就是;在MST加一条边,那么就是n条边了,树中形成了一条环,显然是不行的,那么我们还要在环中删除一条边,那么我们怎么删除才能保证最小了?很显然不可能是删除刚刚加的那一边,因为删除了他就是MST,跟没删一样
,我们要保证权值尽量小,那么我们删除的边就尽量大,我用的是maxb数组来记录的,