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;
    }
  • 相关阅读:
    PowerShell里的数组
    题解 cf gym 103261 D FFT Algorithm
    欧拉筛求积性函数的一般形式
    题解 LOJ 6053
    题解 SP34112 UDIVSUM
    题解 luogu P5162 WD与积木
    Java 关闭流同步输入输出
    题解 luogu P6620 [省选联考 2020 A 卷] 组合数问题
    hdu 6588 题解
    题解 cf gym 102979 E Expected Distance
  • 原文地址:https://www.cnblogs.com/Jadon97/p/8350924.html
Copyright © 2011-2022 走看看