poj-2728Desert King(最优比率生成树)

  题目要求的是MIN( ∑CiXi / ∑DiXi ) Xi∈{0,1},这里xi表示生成树对边的选取,xi=1表示选了,为0表示没有选。

对每个生成树,设其比率r=∑CiXi / ∑DiXi ,可得∑CiXi - ∑DiXi * r=0,可以知道对于所有的生成树,显然有∑CiXi - ∑DiXi * min(r) >= 0,当 ∑CiXi / ∑DiXi = min(r)时,则有∑CiXi - ∑DiXi * min(r) = 0,而我们现在不知道min(r),但是我们知道它满足这个式子(      ∑CiXi - ∑DiXi * min(r) = ∑xi(ci-min(r)*disi)=0,这个式子很明显是一个生成树的边权和,且当r取到MIN(r)时有∑xi(ci-min(r)*disi)=0,从而问题转化为求以ci-rate*disi为边的权的最小生成树,为什么要求最小生成树?因为当rate取得MIN(r)时有生成树的边权和为0,但是我们不知道生成树边权和为0这种情况存不存在,那么就要每次求都求最小生成树,来寻找使得生成树边权和为0存在的可能性。根据 ∑xi(ci-min(r)*disi)=0这个式子存在,我们可以设一个left=0,right为100,在这个区域用二分法求min(rate),1.当我们枚举的rate使得∑xi(ci-rate*disi)=0时,即求得的最小生成树边权和为0,那么rate=MIN(r)。2.当最小生成树的边权和>0则说明该rate对应的所有生成树的边权和都>0,则∑CiXi - ∑DiXi * rate=0不存在,这种情况是rate取小了。3.当最小生成树的边权和<0则说明该rate对应的所有生成树的边权和都<0,则∑CiXi - ∑DiXi * rate=0不存在,这种情况是rate取大了。

#include<iostream>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<iomanip>
using namespace std;
#define maxn 1005
#define inf 0x3f3f3f3f
const double eps=1e-4;
double cost[maxn][maxn],len[maxn][maxn],x[maxn],y[maxn],z[maxn];
double mst,w[maxn][maxn],dis[maxn];
int vis[maxn];
int n;
double getdis(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));

}

bool prim(double r)
{
    for(int i=1;i<=n;i++)
    {
        vis[i]=0;
        dis[i]=inf;
        w[i][i]=0;
        for(int j=i+1;j<=n;j++)
            w[i][j]=w[j][i]=cost[i][j]-r*len[i][j];
    }
    dis[1]=0;
    mst=0;
    while(1)
    {
        int u=-1;
        double minn=inf;
        for(int i=1;i<=n;i++)
            if(!vis[i]&&(u==-1||minn>dis[i]))
            {
                u=i;
                minn=dis[i];

            }
        if(u==-1) break;
        mst+=minn;
        vis[u]=1;
        for(int i=1;i<=n;i++)
        {
            if(!vis[i]&&dis[i]>w[u][i])
            {
                dis[i]=w[u][i];
            }
        }
    }
    if(mst>0) return  true;
    return false;

}
int main()
{
    std::ios::sync_with_stdio(false);
    while(cin>>n)
    {
        if(n==0) break;
        for(int i=1;i<=n;i++)
        {
            cin>>x[i]>>y[i]>>z[i];
        }
        for(int i=1;i<=n;i++)
        {
            cost[i][i]=len[i][i]=0;
            for(int j=i+1;j<=n;j++)
            {
                cost[i][j]=cost[j][i]=fabs(z[i]-z[j]);
                len[i][j]=len[j][i]=getdis(x[i],y[i],x[j],y[j]);
            }
        }
        double left=0,right=100;
        while((right-left)>=eps)
        {
            double mid=(left+right)/2;
            if(prim(mid))
                left=mid;
            else
                right=mid;


        }
        cout<<fixed<<setprecision(3)<<left<<endl;

    }
    return 0;
}

 

posted @ 2018-08-03 08:51  eason99  阅读(77)  评论(0编辑  收藏  举报