【UVALIVE 5713】秦始皇修路(最小瓶颈路+Kruskal)

3 篇文章 0 订阅

传送门

秦始皇修路

I think

    题意:给出n个城市与m条路,求一种修建n-1条路的方案,使得A/B最大。A指可由你选择的一条不用付钱的路i,它连接的两个城市的总人口。B指路i的长度。
    算法:最小瓶颈路
    思路:求出最小生成树。枚举任意两点作为道路(u,v)的两端,若答案边(u,v)不在最小生成树中,连该边即成环,于是删除u,v在MST上的路径中的最长边(最小瓶颈路:两个结点之间一条最长边最短的路径),否则直接将边从最小生成树中删除。
    若数据范围更大,可以用倍增的方法处理任意两点间的最小瓶颈路。

Code

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>

const int sm = 1e3+10;
const int sn = 1e6+10;

int T,N,S,Ct,l;
int hd[sm],to[sm<<1],nxt[sm<<1];
int Fa[sm],X[sm],Y[sm],C[sm];
double mxc[sm][sm];
double Ans,tot,w[sm<<1];
struct Edge {
    int Fm,To;double L;
    bool ex;
}Ed[sn];
std::vector<int>g; 

bool cmp(Edge x,Edge y) { return x.L<y.L; }
template <typename T> T Max(T x,T y) { return x>y?x:y; }
void Ins(int u,int v,double len) { Ed[++S]=(Edge) {u,v,len};}
double Len(int x,int y,int a,int b) { return (double)sqrt((x-a)*(x-a)+(y-b)*(y-b)); }

void read(int &x) {
    x=0;char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}

void init() {
    S=Ct=0,Ans=tot=0;
    l=Max(N,N*(N-1)/2);
    for(int i=1;i<=l;++i) {
        if(i<=N)hd[i]=0;
        Ed[i].ex=0;
    }
    if(!g.empty()) g.clear();
    memset(mxc,0,sizeof(mxc)); 
}
void Add(int u,int v,double len) {
    to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct,w[Ct]=len;
    to[++Ct]=u,nxt[Ct]=hd[v],hd[v]=Ct,w[Ct]=len;
}
int Find(int x) {
    if(x!=Fa[x]) Fa[x]=Find(Fa[x]);
    return Fa[x];
}
void Kruskal() {
    int x,y;
    for(int i=1;i<=N;++i) Fa[i]=i;
    std::sort(Ed+1,Ed+S+1,cmp);
    for(int i=1;i<=S;++i) {
        x=Find(Ed[i].Fm);
        y=Find(Ed[i].To);
        if(x!=y) {
            Add(Ed[i].Fm,Ed[i].To,Ed[i].L);
            Ed[i].ex=1;
            Fa[x]=y,tot+=Ed[i].L;
            if((Ct>>1)==N-1) break;
        }
    }
}
void Dfs(int x,int fa,double LEN) {
    for(int i=0;i<g.size();++i)
        mxc[x][g[i]]=mxc[g[i]][x]=Max(mxc[g[i]][fa],LEN);
    g.push_back(x);
    for(int i=hd[x];i;i=nxt[i])
        if(to[i]!=fa) Dfs(to[i],x,w[i]);
}
void Calc() {
    for(int i=1,x,y,z;i<=S;++i) {
        x=Ed[i].Fm,y=Ed[i].To,z=C[x]+C[y];
        if(Ed[i].ex) Ans=Max(Ans,z*1.0/(tot-Ed[i].L));
        else Ans=Max(Ans,z*1.0/(tot-mxc[x][y]));
    }
}
int main() {
    read(T);
    while(T--) {
        init(),read(N);
        for(int i=1;i<=N;++i)
            read(X[i]),read(Y[i]),read(C[i]);
        for(int i=1;i<N;++i)
            for(int j=i+1;j<=N;++j)
                Ins(i,j,Len(X[i],Y[i],X[j],Y[j]));
        Kruskal(),Dfs(1,0,0),Calc();
        printf("%.2lf\n",Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值