POJ 2728 Desert King(01分数规划+二分+最小生成树-Prim)

Description
有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小
Input
第一行为一整数n表示村庄数量,之后n行每行三个整数x,y,z分别表示村庄的横纵坐标和海拔(2<=n<=1000,0<=x,y<10000,0<=z<10000000)
Output
求费用与距离比值的最小值
Sample Input
4
0 0 0
0 1 1
1 1 2
1 0 3
0
Sample Output
1.000
Solution
最优比率生成树
这里写图片描述
这里写图片描述
显然l随r单调递减,而且l=0时r=ans,因此我们二分r,对于每个r将边权变为cost[i]-r*dis[i],如果这张图最小生成树的权值和小于0说明r偏大,否则r偏小
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 1111
double dis[maxn][maxn],cost[maxn][maxn],mincost[maxn];
int x[maxn],y[maxn],z[maxn];
bool used[maxn];
int V;
double prim(double x)
{
    for(int i=0;i<V;i++)
    {
        mincost[i]=cost[0][i]-x*dis[0][i];
        used[i]=false;
    }
    used[0]=true; 
    double res=0; 
    for(int i=1;i<V;i++)
    {
        int v=-1;
        double minc=INF;
        for(int u=0;u<V;u++) 
            if(!used[u]&&mincost[u]<minc)
                v=u,minc=mincost[u];
        if(v==-1)break;
        used[v]=true;
        res+=minc;
        for(int u=0;u<V;u++) 
            if(!used[u])
                mincost[u]=min(mincost[u],cost[u][v]-x*dis[u][v]); 
    }
    return res;
} 
double get_dis(int x1,int x2,int y1,int y2)
{
    return sqrt(1.0*(x1-x2)*(x1-x2)+1.0*(y1-y2)*(y1-y2));
}
int main()
{
    while(~scanf("%d",&V),V)
    {
        for(int i=0;i<V;i++)scanf("%d%d%d",&x[i],&y[i],&z[i]);
        for(int i=0;i<V;i++)
            for(int j=0;j<V;j++)
                cost[i][j]=abs(z[i]-z[j]),
                dis[i][j]=get_dis(x[i],x[j],y[i],y[j]);
        double l=0,r=100.0;
        while(r-l>1e-4)
        {
            double mid=(l+r)/2;
            if(prim(mid)>=0)l=mid;
            else r=mid;
        }
        printf("%.3f\n",l);
    } 
    return 0;   
}

你可能感兴趣的:(POJ 2728 Desert King(01分数规划+二分+最小生成树-Prim))