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

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

  • 题目大意

    有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小,很显然,这个题目是要求一棵最优比率生成树。

  • 分析
    以下内容摘录自网上(其他博客排版太丑重排了一下)

    概念
    有带权图G, 对于图中每条边e[i], 都有benifiti和costi, 我们要求的是一棵生成树T, 它使得

    (benifit[i])(cost[i])iT

    最大(或最小).这显然是一个具有现实意义的问题.
    解法之一 0-1分数规划
    x[i] 等于1或0, 表示边 e[i] 是否属于生成树.
    则我们所求的比率
    r=(benifit[i]x[i])(cost[i]x[i]),0i<m

    为了使 r 最大, 设计一个子问题: 让 z=(benifit[i]x[i])l(cost[i]x[i])=(d[i]x[i]) (d[i] = benifit[i] - l * cost[i]) , z(l) . z(l)$看做以d为边权的最大生成树的总权值.
    然后明确两个性质:

    1. z单调递减
      证明: 因为cost为正数, 所以z随l的减小而增大.
    2. z(max(r))=0
      证明: 若 z(max(r))<0,(benifit[i]x[i])max(r)(cost[i]x[i])<0 ,
      可化为 max(r)<max(r) 矛盾;
      z(max(r))>=0 , 根据性质1, 当z = 0 时r最大.

  • 代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const double INF=0x3f3f3f3f;
const int  MAXN=1005;
const double eps=1e-7;
int n;
double dist[MAXN];//dist[i]表示当前找到的最小生成子树到节点i的边的最小值
bool vis[MAXN];
double benefit[MAXN][MAXN];//两个村庄之间的收益
double cost[MAXN][MAXN];//两个村庄之间的花费
double edge[MAXN][MAXN];//村庄之间的边权
struct Node
{
    double x,y,z;
}node[MAXN];
void Cal()//计算两两村庄间的收益(距离)和花费(海拔差)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            double tx=(node[i].x-node[j].x);
            double ty=(node[i].y-node[j].y);
            double tz=(node[i].z-node[j].z);
            benefit[i][j]=sqrt(tx*tx+ty*ty);
            cost[i][j]=fabs(tz);
        }
    }
}
double Prim()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)dist[i]=INF;
    double min_edge;
    int min_node;
    double ans=0;//总的生成树边权
    int now=1;//当前处理的节点
    for(int i=1;i1;
        min_edge=INF;
        for(int j=1;j<=n;j++)
        {
            if(vis[j]==0)
            {
                dist[j]=min(dist[j],edge[now][j]);
                if(dist[j] < min_edge)
                {
                    min_edge=dist[j];
                    min_node=j;
                }
            }
        }
        ans+=min_edge;
        now=min_node;
    }
    return ans;
}
double Binary_Work()
{
    double L=0.0;
    double R=100.0;
    double M;
    while(R-L>0.0000001)
    {
        M=(L+R)/2;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                 edge[i][j]=(cost[i][j]-M*benefit[i][j]);
        double t=Prim();
        if(t>=0)L=M;
        else R=M;
    }
    return L;
}
int main()
{
    int x,y,z;//(x,y)表示村庄坐标,z表示村庄海拔
    double ans;
    while(scanf("%d",&n)!=0 && n!=0)
    {
        for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&node[i].x,&node[i].y,&node[i].z);
        Cal();
        ans=Binary_Work();
        printf("%.3f\n",ans);
    }
    return 0;
}
/*
4
0 0 0
0 1 1
1 1 2
1 0 3
0
*/

你可能感兴趣的:(最小生成树)