POJ - 1679 次小生成树//判断最小生成树是不是唯一

 https://blog.csdn.net/niushuai666/article/details/6925258

算法实现流程:

1.首先使用prim算法为基础,在此基础上加入数组max1[i][j],用于记录i到j路径上的权值最大的边。

2.加入stack[i]数组,用于记录加入到MST中的顶点

3.加入pre[i]数组,用于记录加入MST的顶点的相关联边的直接前驱。

4.在找到temp和k后,需要进行一次循环,用于更新新加入点到MST各点路径最大值(作用在于,如果我要在这个顶点加边形成环,我需要去掉这个环上权值最大的边)至于为什么要用

max(max1[stack[j]][pre[k]], temp)

比较的是pre[k],因为加入的k点如果要跟MST其他点连接,必须通过它的直接前驱,而它的前驱一定是MST中已经确定的到各个顶点都是最大值的记忆化状态,所以我们要比较的是pre[k],而后面之所以是temp因为,temp是MST和非MST两个集合相连的最小权值的边,但是你要加入MST,不能保证他是MST中权值最小的(甚至是最大的)所以加入时需要用temp和max1[stack[j][pre[k]]比较,来得到max1[i][j](i到j路径上权值最大的边)

5.保存一下加入到MST中的顶点

6.更新lowxost,并且记录下更新后的直接前驱

7.prim算法结束后,max1数组存放的就是MST中各个顶点之间的权值最大的边。这时,我们需要对MST外的边加入到MST中并删除最大边的操作。这时候我们需要一个双层循环,遍历每一个MST外的边,当然,必须保证是MST外的边,那个判断语句就是防止是MST种的边,至于为什么i != pre[j] 和 j != pre[i]同时存在,是为了防止出现i=3,j=2和j=2,i=3,这种情况。这个判断一定要正确,要不然就会出现错误。

#include "iostream"
#include "algorithm"
#include "cstring"
using namespace std;
int vst[105],mapp[105][105],dis[105];
int pre[105],stackk[105],maxx[105][105];
int n,m,ans,minn,k;
void init(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            mapp[i][j]=1<<29;
            maxx[i][j]=0;
        }
}

void prim(){
    int k,top=0;
    ans=0;
    memset(vst,0,sizeof(vst));
    for(int i=1;i<=n;i++){
        dis[i]=mapp[1][i];
        pre[i]=1;
    }
    dis[1]=0;vst[1]=1;
    stackk[top++]=1;
    for(int i=1;i<n;i++){
        minn=1<<29,k;
        for(int j=1;j<=n;j++)
            if(!vst[j]&&minn>dis[j]){
                minn=dis[j];
                k=j;
            }
        if(minn==1<<29){
            ans=-1;
            return ;
        }
        ans+=minn;
        vst[k]=1;
        for(int j=0;j<top;j++)
            maxx[stackk[j]][k]=maxx[k][stackk[j]]=max(maxx[stackk[j]][pre[k]],minn);
        stackk[top++]=k;
        for(int j=1;j<=n;j++)
            if(!vst[j]&&dis[j]>mapp[k][j]){
                dis[j]=mapp[k][j];
                pre[j]=k;
            }
    }
}

int main (){
        int T,x,y,w;
        cin>>T;
        while(T--){
            cin>>n>>m;
            init();
            while(m--){
                cin>>x>>y>>w;
                mapp[x][y]=mapp[y][x]=w;
            }
        prim();
        minn=1<<29;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				if(i!=j&&i!=pre[j]&&j!=pre[i]) //枚举MST以外的边
					minn=min(minn,mapp[i][j]-maxx[i][j]); //求出{MST外加入边-MST环上权值最大边}最小值
		if(minn!=0)
			cout<<ans<<endl;
		else
			cout<<"Not Unique!"<<endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值