zoukankan      html  css  js  c++  java
  • poj 2728 Desert King(最小比率生成树,迭代法)

    引用别人的解释:

    题意:有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,

             建造水管距离为坐标之间的欧几里德距离(好象是叫欧几里德距离吧),费用为海拔之差

             现在要求方案使得费用与距离的比值最小

    很显然,这个题目是要求一棵最优比率生成树,



    概念

    有带权图G, 对于图中每条边e[i], 都有benifit[i](收入)和cost[i](花费), 我们要求的是一棵生成树T, 它使得 ∑(benifit[i]) / ∑(cost[i]), i∈T 最大(或最小).

    这显然是一个具有现实意义的问题.

    解法之一 0-1分数规划

    设x[i]等于1或0, 表示边e[i]是否属于生成树.

    则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<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最大.

    到了这个地步, 七窍全已打通, 喜欢二分的上二分, 喜欢Dinkelbach的就Dinkelbach.

    复杂度

    时间 O( O(MST) * log max(r) )

    空间 O( O(MST) )


    迭代+prim

    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<limits.h>
    #define MAX 1100
    double x[MAX],y[MAX],z[MAX];
    double cost[MAX][MAX],dist[MAX][MAX];
    int n;
    double prim(double);
    int main(void)
    {
        int i,j;
        while(scanf("%d",&n)&&n){
            for(i=1;i<=n;i++){//读取数据
                scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
            }
            //处理 任意点之间的长度和价值
            for(i=1;i<=n;i++){ 
                for(j=i+1;j<=n;j++){
                    double d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
                    dist[i][j]=dist[j][i]=sqrt(d);
                    double c=z[i]-z[j];
                    if(c<0) c=-c;
                    cost[i][j]=cost[j][i]=c;                
                }
            }
            //判断给定的rate是否在误差之内
            double a=0,b=0; //初始r为0
            while(1)
            {
                b = prim(a);
                if(fabs(a-b)<1e-4)  break;
                a=b;
            }         
            printf("%.3lf
    ",b);  
            
        }
        return 0;
    }
    double prim(double p)
    {
        int visit[MAX],father[MAX];
        double dis[MAX];
        int i,j;
        //访问数组初始化
        memset(visit,0,sizeof(visit));
        visit[1]=1;
        //对已加入的最小生成树的顶点集合 计算它的父节点
        for(i=2;i<=n;i++){
            dis[i]=cost[1][i]-p*dist[1][i];
            father[i]=1;
        }    
                
        int k=0;
        double totalCost=0,totalDist=0;
        //prim求最小生成树
        for(i=1;i<n;i++){
            k=0;
            double mincost=INT_MAX;//最大值,最好是这个
    
            for(j=2;j<=n;j++){
                if(!visit[j]&&dis[j]<mincost) {
                    mincost=dis[j];k=j;
                }
            }
            
            if(k==0) break;
    
            visit[k]=1;
            totalCost+=cost[ father[k] ][k];
            totalDist+=dist[ father[k] ][k];
            
            for(j=1;j<=n;j++){
                double h=cost[k][j]-p*dist[k][j];
                if(!visit[j]&&dis[j]>h){
                    dis[j]=h;
                    father[j]=k;
                }
            }
            
        }
        return totalCost/totalDist;
    }
  • 相关阅读:
    开源协议
    markdown 语法和工具
    mac 终端 使用ftp命令
    谷歌囧大了!安统镜卓5.0刷入遇到system.img系像找不到
    linux下vi命令大全
    python编码处理:unicode字节串转成中文 各种字符串举例说明
    优秀前端资源备忘录
    mousewheel滚轮事件
    bootstrap插件之Carousel
    初识Node.js
  • 原文地址:https://www.cnblogs.com/woshijishu3/p/3854158.html
Copyright © 2011-2022 走看看