POJ 1679 The Unique MST(次小生成树)

Description
给出一张连通的无向图,判断其最小生成树是否唯一
Input
第一行为一整数T表示用例组数,每组用例第一行为两整数n和m表示该无向图的点数和边数,之后m行每行三个整数a,b,c表示点a与点b之间有一条权值为c的边
Output
对于每组用例,如果该图的最小生成树唯一则输出最小生成树权值和,否则输出Not Unique!
Sample Input
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
Sample Output
3
Not Unique!
Solution
求出最小生成树权值和mst和次小生成树权值和secmst,如果两者相等说明最小生成树不唯一,否则唯一,所以问题转化为如何求次小生成树,首先给出一个结论:设T为无向图G的最小生成树,那么G的次小生成树可以由T删去一条边再添加一条边构成。如果枚举这被删去和被添加的边,时间复杂度太高,一种效率比较高的做法是首先将不属于T的一条边(x,y)加到T中,那么T一定成环,那么删去环上除(x,y)之外权值最大的一条边可以得到加入(x,y)后权值最小的一棵树,如果能够很快的得到最小生成树上两点之间最长边的权值,那么枚举(x,y)就可以快速得到次小生成树,而在用Kruskal算法求最小生成树的过程中,可以在算法运行过程中求出x到y路径上的最长边,因为每次合并两个等价类的时候,分属于两等价类的点之间的最长边一定为当前新加入的边,这样我们就可以记录任意两点之间路径上的最大值,问题得到很好的解决
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 1111
double dis[maxn][maxn],cost[maxn][maxn],mincost[maxn];
int x[maxn],y[maxn],z[maxn];
bool used[maxn];
int V;
double prim(double x)
{
    for(int i=0;i<V;i++)
    {
        mincost[i]=cost[0][i]-x*dis[0][i];
        used[i]=false;
    }
    used[0]=true; 
    double res=0; 
    for(int i=1;i<V;i++)
    {
        int v=-1;
        double minc=INF;
        for(int u=0;u<V;u++) 
            if(!used[u]&&mincost[u]<minc)
                v=u,minc=mincost[u];
        if(v==-1)break;
        used[v]=true;
        res+=minc;
        for(int u=0;u<V;u++) 
            if(!used[u])
                mincost[u]=min(mincost[u],cost[u][v]-x*dis[u][v]); 
    }
    return res;
} 
double get_dis(int x1,int x2,int y1,int y2)
{
    return sqrt(1.0*(x1-x2)*(x1-x2)+1.0*(y1-y2)*(y1-y2));
}
int main()
{
    while(~scanf("%d",&V),V)
    {
        for(int i=0;i<V;i++)scanf("%d%d%d",&x[i],&y[i],&z[i]);
        for(int i=0;i<V;i++)
            for(int j=0;j<V;j++)
                cost[i][j]=abs(z[i]-z[j]),
                dis[i][j]=get_dis(x[i],x[j],y[i],y[j]);
        double l=0,r=100.0;
        while(r-l>1e-4)
        {
            double mid=(l+r)/2;
            if(prim(mid)>=0)l=mid;
            else r=mid;
        }
        printf("%.3f\n",l);
    } 
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值