hdu4081Qin Shi Huang's National Road System

本文解析了HDU 4081题目,通过求最小生成树并枚举删边来最大化特定比值的问题。介绍了使用结构体存储边信息,并通过DFS进行树的遍历的方法。

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081

题意:给定n个点,每个点有点权,求生成树,然后删掉一条边增加一条魔法边使得魔法边连的两个节点的点权和除以剩余边的和最大。

分析:先求最小生成树,枚举删哪条边和删完后两颗子树中的最大点权,魔法边就是连这条个点。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1010;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const ll MAX=1ll<<55;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
struct node {
    int a,b;
    db w;
    bool operator < (const node x) const {
        return w<x.w;
    }
}f[500010];
db w[2*N],fd[N];
int x[N],y[N],p[N],fa[N],mx[N],d[N][12];
int tot,in[N],out[N],u[N],v[2*N],pre[2*N];
void add(int a,int b,db c) {
    v[tot]=b;w[tot]=c;pre[tot]=u[a];u[a]=tot++;
}
db dis(int a,int b) {
    return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
int find_f(int a) {
    return fa[a]==a?a:find_f(fa[a]);
}
void dfs(int a,int b,db c) {
    in[a]=++tot;d[tot][0]=p[a];
    mx[a]=p[a];fa[a]=b;fd[a]=c;
    for (int i=u[a];i!=-1;i=pre[i])
    if (v[i]==b) continue ;
    else { dfs(v[i],a,w[i]);mx[a]=max(mx[a],mx[v[i]]); }
    out[a]=tot;
}
void dealrmq(int n) {
    int i,j;
    for (i=1;i<12;i++)
        for (j=1;j<=n;j++)
        if (j+(1<<i)-1>n) break ;
        else d[j][i]=max(d[j][i-1],d[j+(1<<(i-1))][i-1]);
}
int get(int l,int r) {
    if (l>r) return 0;
    int w=(int)log2(r-l+1);
    return max(d[l][w],d[r-(1<<w)+1][w]);
}
int main()
{
    int i,j,k,n,t,f1,f2;
    db sum,ans;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (i=1;i<=n;i++) scanf("%d%d%d", &x[i], &y[i], &p[i]);
        for (k=0,i=1;i<=n;i++)
            for (j=i+1;j<=n;j++) {
                k++;f[k].a=i;f[k].b=j;f[k].w=dis(i,j);
            }
        for (i=1;i<=n;i++) fa[i]=i;
        sort(f+1,f+k+1);
        sum=tot=0;
        memset(u,-1,sizeof(u));
        for (i=1;i<=k;i++) {
            f1=find_f(f[i].a);f2=find_f(f[i].b);
            if (f1!=f2) {
                sum+=f[i].w;fa[f1]=f2;
                add(f[i].a,f[i].b,f[i].w);
                add(f[i].b,f[i].a,f[i].w);
            }
        }
        ans=tot=0;
        dfs(1,0,0);
        dealrmq(n);
        for (i=2;i<=n;i++) ans=max(ans,1.0*(mx[i]+max(get(1,in[i]-1),get(out[i]+1,n)))/(sum-fd[i]));
        printf("%.2f\n", ans);
    }
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值