Desert King POJ - 2728(最優比率生成樹)

題目:

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,代表在沙漠中有N個村莊;後面有N組數據,每個數據三個數字X,Y,Z代表村莊在位置(X,Y),且村莊的海拔是Z;

國王想要給每一個村莊供水,沒修建一條路的花費是兩個村莊之間的海拔之差;

問你想要使每一個村莊的都連接起來,建成的路的平均每公里的花費最小;

思路:

其實這道題是讓你求出來  ai / bi  所有和的最小值;

那麼假設 ai / bi >=x,那麼ai - x*bi >= 0;

這道題可以使用最優比率生成樹算法,其中用二分法來枚舉  合適的 x 的值;

令F(x)=ai - x*bi,那麼這就可以看成一個一元一次的方程式:F(x)=A - x*B,即F(x)=  - B *x + A;

還是看這個圖,用二分來枚舉f(x) ;

因爲是要求最小生成樹,所以在每一次的二分判斷中要跑一遍prim算法來判斷現在的最小生成樹的值是否小於0;

如果小於0,那麼就是二分區間大了,縮小區間;

如果大於0,那麼就是二分區間小了,擴大區間;

代碼如下:

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const double inf=1e18;
const int maxn=1e3+10;

int n;
double cost[maxn][maxn];
double dis[maxn][maxn];
double x[maxn],y[maxn],z[maxn];
int vis[maxn];

double get_dis(int a,int b)///計算兩點之間的距離;
{
    return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}

int check(double x)
{
    memset(vis,0,sizeof vis);
    double sum=0,lowcost[maxn];
    vis[1]=1;
    for(int i=1; i<=n; i++)///計算最小的每一單位距離的花費;
        lowcost[i]=cost[1][i]-x*dis[1][i];
    ///prim算法;
    for(int i=2; i<=n; i++)///最小生成樹,n-1條邊;
    {
        double temp=inf;
        int k=-1;///記錄是否還有最小的邊;
        for(int j=2; j<=n; j++)
        {
            if(!vis[j]&&lowcost[j]<temp)
            {
                k=j;
                temp=lowcost[j];
            }
        }
        if(k==-1)
            break;
        vis[k]=1;
        sum+=temp;
        for(int j=2; j<=n; j++)///鬆弛;
        {
            if(!vis[j]&&cost[k][j]-x*dis[k][j]<lowcost[j])
                lowcost[j]=cost[k][j]-x*dis[k][j];
        }
    }
    if(sum>=0)
        return 1;
    else
        return 0;
}

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
        }
        for(int i=1; i<=n; i++)///存儲圖的數據;
        {
            for(int j=i+1; j<=n; j++)
            {
                dis[i][j]=dis[j][i]=get_dis(i,j);
                cost[i][j]=cost[j][i]=fabs(z[i]-z[j]);
            }
        }
        ///二分枚舉;
        double l=0.0,r=100.0;
        while(r-l>=1e-5)
        {
            double mid=(l+r)/2;
            if(check(mid))
                l=mid;///擴大區間;
            else
                r=mid;///縮小區間;
        }
        printf("%.3f\n",l);///注意輸出格式;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章