POJ 2728 最优比例生成树 01分数规划 模板 迭代+二分

版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/82353083

Desert King

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 30398   Accepted: 8377

Description

David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way. 

After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital. 

His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can't share a lifter. Channels can intersect safely and no three villages are on the same line. 

As King David's prime scientist and programmer, you are asked to find out the best solution to build the channels.

Input

There are several test cases. Each test case starts with a line containing a number N (2 <= N <= 1000), which is the number of villages. Each of the following N lines contains three integers, x, y and z (0 <= x, y < 10000, 0 <= z < 10000000). (x, y) is the position of the village and z is the altitude. The first village is the capital. A test case with N = 0 ends the input, and should not be processed.

Output

For each test case, output one line containing a decimal number, which is the minimum ratio of overall cost of the channels to the total length. This number should be rounded three digits after the decimal point.

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000

题意: 

       给你n个二维坐标和一个高度,两两个点之间的价值是坐标之间的曼哈顿距离,费用是高度差,要你求联系起来的最大的费用和/价值和最小。(即单位价值的费用最少)

扫描二维码关注公众号,回复: 3094075 查看本文章

做法:

       最近刚刚学了01规划,看了普通的分数规划之后发现了最优比例生成树,其实就是先二分最优的比例,然后在这个比例下去计算相应的最小生成树的最大权值,如果这个权值为正那么还能有更优秀的比例。

       因为怕忘记所以还是列一下式子好了。要求Ci/Vi 的最小,即另f(x)=c_{i}-x*v_{i}最小,所以如果f(x)>0,那么一定还有一个更大的x让f(x)最小(很明显f(x)是一个单调递减函数),那么只要在二分的时候让l=mid,就好,反之让r=mid;

        迭代的做法我还没有完全理解,先贴一个模板。是真的可怕 时间差了九倍。


二分代码如下:

    1776ms

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define inf 1000000000
using namespace std;
const int maxn=1005;
const double eps=1e-9;
struct node{
    double x,y,h;
}p[maxn];
int n,pre[maxn];
double lowcost[maxn],dis[maxn][maxn];
double cal(int a,int b){
    return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}
int ck(int src,double mid){
    double ans=0;
    for(int i=1;i<=n;i++){
        lowcost[i]=abs(p[src].h-p[i].h)-mid*dis[i][src];
        pre[i]=src;
    }
    pre[src]=-1;
    for(int i=1;i<n;i++){
        double nowans=inf;
        int aim=-1;
        for(int j=1;j<=n;j++){
            if(pre[j]!=-1&&lowcost[j]<nowans){
                nowans=lowcost[j];
                aim=j;
            }
        }
        if(aim!=-1){
            ans+=lowcost[aim];
            pre[aim]=-1;
            for(int j=1;j<=n;j++){
                double tmp=abs(p[aim].h-p[j].h)-dis[aim][j]*mid;
                if(pre[j]!=-1&&lowcost[j]>tmp){
                    lowcost[j]=tmp;
                    pre[j]=aim;
                }
            }
        }
    }
    return ans>-eps;
}
int main(){
    while(~scanf("%d",&n)){
        if(n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].h);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i==j) continue;
                else   dis[i][j]=cal(i,j);
        double l=0,r=100.0;
        while(r-l>eps){
            double mid=(l+r)/2;
            if(ck(1,mid)) l=mid;
            else r=mid;
        }
        printf("%.3f\n",r);
    }
    return 0;
}

迭代代码如下:

    219ms  (这时间差的真可怕。。)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define inf 1000000000
using namespace std;
const int maxn=1005;
const double eps=1e-9;
struct node{
    double x,y,h;
}p[maxn];
int n,pre[maxn];
double lowcost[maxn],dis[maxn][maxn];
double cal(int a,int b){
    return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}

double prim(int src,double mid){
    double sumcost=0,sumval=0;
    for(int i=1;i<=n;i++){
        lowcost[i]=abs(p[src].h-p[i].h)-mid*dis[i][src];
        pre[i]=src;
    }
    pre[src]=-1;
    for(int i=1;i<n;i++){
        double nowans=inf;
        int aim=-1;
        for(int j=1;j<=n;j++){
            if(pre[j]!=-1&&lowcost[j]<nowans){
                nowans=lowcost[j];
                aim=j;
            }
        }
        if(aim!=-1){
            sumcost+=abs(p[aim].h-p[pre[aim]].h);
            sumval+=dis[aim][pre[aim]];
            pre[aim]=-1;
            for(int j=1;j<=n;j++){
                double tmp=abs(p[aim].h-p[j].h)-dis[aim][j]*mid;
                if(pre[j]!=-1&&lowcost[j]>tmp){
                    lowcost[j]=tmp;
                    pre[j]=aim;
                }
            }
        }
    }
    return sumcost/sumval;
}

int main(){
    while(~scanf("%d",&n)){
        if(n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].h);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i==j) continue;
                else   dis[i][j]=cal(i,j);
        double a=0,b;
        while(1){
            b=prim(1,a);
            if(fabs(a-b)<eps) break;
            a=b;
        }
        printf("%.3f\n",a);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41955236/article/details/82353083
今日推荐