• 最小生成树求最大比率 UVALive


    题目链接:https://vjudge.net/problem/UVALive-5713

    题意:给出t组数据,每组数据第一行给出一个n,表示点的数量,接下来n行,每行有三个数字,分别是点的坐标x,y和点的值w。现在我们要用n-1条边来连接这n个点,秦始皇希望这n-1条边的权值之和最小,现在徐福说他可以让其中一条边的权值为0,但是他希望这条边所连接的两点的值之和最大,所以秦始皇决定使A/B的值最大,其中,A是徐福选择的那条边所连接两点的值的和,B是除徐福选择的边之外,其他n-2条边的权值之和,输出最大的A/B。

    以下内容是书上面看的。

    思路:要使A/B最大,所以A要尽量大,B要尽量小。我们先让B尽量小,所以我们先求出最小生成树的权值之和假设是sum,那么我们只要枚举最小生成树的每一条边,把这条边去掉,假设这条边权值是s,让它变成两棵树,分别在两颗树里面找到值最大的点(假设两个点的值分别是a,b),结果就是max{(a+b)*1.0/(sum-s)}。

    对于sum,我们用prime算法计算出来,同时记录每个点的前驱,然后枚举我们所要去掉的边的时候,我们可以用dfs标记,把最小生成树分成两颗树。

    看代码吧:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long LL;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 1005
    int n,m,k,t;
    double map[maxn][maxn],dis[maxn];
    int pre[maxn];
    double sum;
    int vis[maxn]; 
    struct node{
        double x,y;
        int w;
    }point[maxn];
    double cal_dis(int i,int j){//计算两点距离 
        return sqrt(1.0*(point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
    }
    void prime(){
        memset(vis,0,sizeof(vis));
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n;i++)
        dis[i]=INF;
        dis[1]=0;
        sum=0;
        for(int i=1;i<=n;i++){
            double min1=INF;
            int u;
            for(int j=1;j<=n;j++){
                if(!vis[j]&&dis[j]<min1){
                    min1=dis[j];
                    u=j;
                }
            }
            if(min1==INF)
            return;
            vis[u]=1;
            sum+=min1;
            for(int j=1;j<=n;j++){
                if(!vis[j]&&map[u][j]<dis[j]){
                    dis[j]=map[u][j];
                    pre[j]=u;//记录点j的前驱 
                }
            }
        }
    }
    void DFS(int u){
        vis[u]=1;
        for(int i=1;i<=n;i++){
            if(!vis[i]&&pre[i]==u)
            DFS(i);
        }
    }
    double solve(){
        double ans=0;
        for(int i=1;i<=n;i++){
            if(pre[i]==0)//我们选择连接点i和点i的前驱的这条边,如果点i没有前驱continue 
            continue;
            memset(vis,0,sizeof(vis));
            DFS(i);//标记以点i根节点的树(标记为1) 
            double s=map[i][pre[i]];//记录去掉的这条边的权值 
            int a=0,b=0;
            for(int j=1;j<=n;j++){
                if(vis[j])//属于以点i为根的树 
                a=max(a,point[j].w);
                else//属于以点pre[i]为根的树 
                b=max(b,point[j].w);
            }
            ans=max(ans,(a+b)*1.0/(sum-s));
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            scanf("%lf%lf%d",&point[i].x,&point[i].y,&point[i].w);
            memset(map,0,sizeof(map));
            for(int i=1;i<n;i++){
                for(int j=i+1;j<=n;j++){
                    map[i][j]=map[j][i]=cal_dis(i,j);
                }
            }
            prime();
            printf("%.2lf
    ",solve());
        }
        return 0;
    }
  • 相关阅读:
    Mybatis中的设计模式
    Mybatis的#{}和${}的区别是什么?
    ES的写入速度优化
    康师傅JVM:垃圾回收相关概念(十六)
    i++为什么不是原子操作?
    Zookeeper的watch机制
    LeetCode 1.两数之和
    ESP 8266 引脚图
    Arduino 将 String 转化为 int
    微擎修改 icon.jpg 后项目主页未变
  • 原文地址:https://www.cnblogs.com/6262369sss/p/10029641.html
Copyright © 2020-2023  润新知