zoukankan      html  css  js  c++  java
  • POJ 2728 Desert King(最优比率生成树, 01分数规划)

    题意:

    给定n个村子的坐标(x,y)和高度z, 求出修n-1条路连通所有村子, 并且让 修路花费/修路长度 最少的值

    两个村子修一条路, 修路花费 = abs(高度差), 修路长度 = 欧氏距离

    分析:

    01分数划分的题目, 构造出 d[i] = 修路花费 - L * 修路长度, 这个L值我们可以二分(这道题看数据范围的话二分上限其实挺大的, 但其实上限取到100就可以过), 也可以用Dinkelbach迭代出来。

      二分(1422ms)

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <cstring>
    #include <cmath>
    #define rep(i,a,b) for(int i = a; i < b;i++)
    #define _rep(i,a,b) for(int i = a; i <= b;i++)
    using namespace std;
    const int maxn = 1000 + 7;
    const double inf = 1e9 + 7;
    const double eps = 1e-5;
    int n;
    
    double G[maxn][maxn];
    double x[maxn], y[maxn], z[maxn];
    
    inline double p2p_dis(double x1, double y1, double x2, double y2){
        return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    double prim(double L){
        double tree_dis = 0;
        double dis[maxn];
        int vis[maxn];
        fill(dis, dis + maxn, inf);
        memset(vis, 0 ,sizeof(vis));
    
        dis[0] = 0;
        vis[0] = 1;
    
        for(int i = 1; i < n; i++) dis[i] =  abs(z[0] - z[i]) - L * G[0][i];
    
    
        for(int times = 0; times < n - 1; times++){
            int picked = -1;
            double min_dis = inf;
            for(int i = 0; i < n; i++){
                if(!vis[i] && dis[i] < min_dis){
                    min_dis = dis[i];
                    picked = i;
                }
            }
            tree_dis += min_dis;
            vis[picked] = 1;
            for(int i = 0; i < n; i++)
                if(!vis[i])
                    dis[i] = min(dis[i] , abs(z[picked] - z[i]) - L * G[picked][i]) ;
        }
        return tree_dis;
    }
    
    int main(){
    //    freopen("1.txt","r", stdin);
        while(cin >> n && n){
            rep(i,0,n) cin >> x[i] >> y[i] >> z[i];
            rep(i,0,n) rep(j,i+1,n) G[i][j] = G[j][i] = p2p_dis(x[i],y[i],x[j],y[j]);
            double l = 0, r = 14143000;
            while(abs(r - l) > eps){
                double mid = (l + r) / 2;
    //            printf("%.3f
    ", mid);
                if(prim(mid) >= 0){
                    l = mid;
                }else{
                    r = mid;
                }
            }
            printf("%.3f
    ", l);
        }
        return 0;
    }

    Dinkelbach(204ms)

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <cstring>
    #include <cmath>
    #define rep(i,a,b) for(int i = a; i < b;i++)
    #define _rep(i,a,b) for(int i = a; i <= b;i++)
    using namespace std;
    const int maxn = 1000 + 7;
    const double inf = 1e9 + 7;
    const double eps = 1e-5;
    int n;
    
    double G[maxn][maxn];
    double x[maxn], y[maxn], z[maxn];
    
    inline double p2p_dis(double x1, double y1, double x2, double y2){
        return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    double prim(double L){
        double ele = 0, deno = 0; //分子分母
        double dis[maxn];
        int near[maxn]; //开一个数组记录每次选中是哪一条边
    
        int vis[maxn];
        fill(dis, dis + maxn, inf);
        memset(vis, 0 ,sizeof(vis));
        memset(near, -1, sizeof(near));
        dis[0] = 0;
        vis[0] = 1;
    
        for(int i = 1; i < n; i++) dis[i] =  abs(z[0] - z[i]) - L * G[0][i], near[i] = 0; //每个点都是由0点更新的, 所以near都是0
    
        for(int times = 0; times < n - 1; times++){
            int picked = -1;
            double min_dis = inf;
            for(int i = 0; i < n; i++){
                if(!vis[i] && dis[i] < min_dis){
                    min_dis = dis[i];
                    picked = i;
                }
            }
            ele += abs(z[near[picked]] - z[picked]);//分子是选的路程
            deno += G[near[picked]][picked]; //分母是真实的花费
            vis[picked] = 1;
            for(int i = 0; i < n; i++)
                if(!vis[i] && dis[i] > abs(z[picked] - z[i]) - L * G[picked][i]){
                    dis[i] = abs(z[picked] - z[i]) - L * G[picked][i];
                    near[i] = picked;//如果通过picked点更新dis,那么该位置near就是picked
                }
    
        }
    
        return ele/deno; //注意返回的跟二分不一样, 应该返回当前L下真正题目的所求
    }
    
    int main(){
    //    freopen("1.txt","r", stdin);
        while(cin >> n && n){
            rep(i,0,n) cin >> x[i] >> y[i] >> z[i];
            rep(i,0,n) rep(j,i+1,n) G[i][j] = G[j][i] = p2p_dis(x[i],y[i],x[j],y[j]);
            double L, ans = 0;
            while(1){
                L = ans;
                ans = prim(L); //这是一个真实值, 不是含L的变式
                if(abs(ans - L)< eps) break;
            }
            printf("%.3f
    ", ans);
        }
        return 0;
    }
  • 相关阅读:
    BZOJ3752 : Hack
    XIV Open Cup named after E.V. Pankratiev. GP of SPb
    XIII Open Cup named after E.V. Pankratiev. GP of Ukraine
    BZOJ2087 : [Poi2010]Sheep
    BZOJ2080 : [Poi2010]Railway
    BZOJ2082 : [Poi2010]Divine divisor
    Moscow Pre-Finals Workshop 2016. National Taiwan U Selection
    XIII Open Cup named after E.V. Pankratiev. GP of Asia and South Caucasus
    XIII Open Cup named after E.V. Pankratiev. GP of Azov Sea
    XIII Open Cup named after E.V. Pankratiev. GP of SPb
  • 原文地址:https://www.cnblogs.com/Jadon97/p/8350924.html
Copyright © 2011-2022 走看看