POJ2728 Desert King ——01分数规划Dinkelbach迭代法+最小生成树prim算法

  首先,纪念我用Linux系统AC的第一题…
  安装这个万恶的NOI Linux系统费了6小时的时间,不过好在最后终于装上了,但是因为我安装的Linux系统比较烂,还遭到了小花儿和js的鄙视,唉,本人蒟蒻,有什么办法呢…和SJZEZ的模拟赛Day2杯具,少考虑了一个条件丢掉100分,总分368,Rank3,被小花儿和SJZEZ的LYD神犇分别以480分和435分虐了,Day1还被SXY神犇虐了7分,还好Day2追回来了,总分比SXY神犇多27分险居Rank3;CL2姐姐同杯具,Rank5。非常奇葩的是,两校排名冠亚军是男生,第三名到第五名被两校仅有的三个参加考试的女生占据…嗯,也算给NOIP涨了信心了吧。NOIP的目标很明确:拿省一、并尽量拿高分为省选做准备。
  说说这道题吧:01分数规划的Dinkelbach迭代法+最小生成树的prim算法
  题意:给出几个村庄的坐标x[i]和y[i],以及海拔z[i]。要在这些村庄之间建水渠,费用和两个村庄的海拔差成正比,水渠长度和村庄二维坐标(x,y)上的距离成正比,要求一种方案使得(总的花费/总的水渠长度)最小,输出这个最小值,保留三位小数。
  算法讨论:经典的01分数规划。采用Dinkelbach迭代法比较快。01分数规划请参考本博:http://www.cnblogs.com/Thispoet/archive/2011/09/19/2181377.html
  由于这是一个稠密图,采用prim算法较好。
  迭代答案ans,定义a[i]=c[i]-d[i]*ans(相当于平常prim算法的lowcost数组),c[i]为a[i]取到最小值的时候i这个点到达集合的距离,d[i]为a[i]取到最小值时i这个点到达集合所需要的费用,每次取a[i]最小的加入集合当中,并利用它来更新其他点。定义temp=sigma(c[i])/sigma(d[i]),最后当ans和temp的差值达到精度要求的时候输出,否则令ans=temp继续上一步,直到满足要求为止。
  代码写得不长,而且很顺利,也许是刚装上Linux激动的状态很好吧…

program poj2728;//By_Thispoet

const

 maxn=1005;

var

 i,j,n,p,pos                 :longint;

 x,y,z                       :array[0..maxn]of longint;

 a,d,c                       :array[0..maxn]of extended;

 ans,temp,t,k,m,q            :extended;

 flag                        :array[0..maxn]of boolean;



begin

 readln(n);

 while not (n=0) do

  begin

   for i:=1 to n do

    readln(x[i],y[i],z[i]);

   ans:=0;temp:=0;

   repeat

    fillchar(flag,sizeof(flag),0);

    flag[1]:=true;ans:=temp;

    for i:=2 to n do

     begin

      c[i]:=abs(z[i]-z[1]);

      d[i]:=sqrt(sqr(x[i]-x[1])+sqr(y[i]-y[1]));

      a[i]:=c[i]-d[i]*ans;

     end;

    p:=1;m:=0;q:=0;

    while p<n do begin

      temp:=maxlongint;

      for i:=2 to n do if (not flag[i])and(a[i]<temp) then begin

       temp:=a[i];pos:=i;

      end;

      flag[pos]:=true;m:=m+c[pos];q:=q+d[pos];

      for i:=2 to n do if not flag[i] then begin

       t:=abs(z[i]-z[pos]);k:=sqrt(sqr(x[i]-x[pos])+sqr(y[i]-y[pos]));

       if (t-k*ans)<a[i] then begin

        a[i]:=t-k*ans;c[i]:=t;d[i]:=k;

       end;

      end;

      inc(p);

     end;

    temp:=m/q;

   until abs(ans-temp)<0.000001;

   writeln(ans:0:3);readln(n);

  end;

end.

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