poj 2976 && poj 2728 && poj3621 && bzoj 1486 01分数规划

来源:互联网 发布:淘宝客服下班结束语 编辑:程序博客网 时间:2024/05/18 00:48

关于01分数规划的具体讲解和例题分析,推荐相当好的博客:http://www.cnblogs.com/perseawe/archive/2012/05/03/01fsgh.html

这里不再赘述

所以这里只是简单粗暴的贴代码


poj 2976

题目的要求是求比值最大,限制是所取得数量为n-k,所以只需要将d排序取前n-k个即可

注意:数据结束的条件是(n=0) && (k=0),但是在读入的时候不能判断 while (n<>0) and  (k<>0) ,因为会存在合法的输入使得 n<>0 && k=0 ,所以数据结束的条件是(n<>0)

二分方法:

const        eps=1e-6;var        n,m,k           :longint;        i               :longint;        tmp,l,r,mid     :double;        a,b             :array[0..1010] of longint;        d               :array[0..1010] of double;procedure sort(l,r:longint);var        i,j:longint;        x,y:double;begin   i:=l; j:=r; x:=d[(l+r)>>1];   while (i<=j) do   begin      while d[i]>x do inc(i);      while d[j]<x do dec(j);      if i<=j then      begin         y:=d[i]; d[i]:=d[j]; d[j]:=y;         inc(i); dec(j);      end;   end;   if i<r then sort(i,r);   if j>l then sort(l,j);end;begin   read(n,k);   while (n<>0) do   begin      for i:=1 to n do read(a[i]);      for i:=1 to n do read(b[i]);      m:=n-k;      r:=0;      for i:=1 to n do if a[i]/b[i]>r then r:=a[i]/b[i];      l:=0;      while (r-l>=eps) do      begin         mid:=(l+r)/2;         for i:=1 to n do d[i]:=a[i]-mid*b[i];         sort(1,n);         tmp:=0;         for i:=1 to m do tmp:=tmp+d[i];         if tmp>0 then l:=mid else r:=mid;      end;      writeln(round(l*100));      read(n,k);   end;end.

Dinkelbach算法:

const        eps=1e-6;var        n,m,k           :longint;        i               :longint;        l,ans           :double;        p,q             :int64;        a,b,c           :array[0..1010] of longint;        d               :array[0..1010] of double;procedure sort(l,r:longint);var        i,j:longint;        z:longint;        x,y:double;begin   i:=l; j:=r; x:=d[(l+r)>>1];   while (i<=j) do   begin      while d[i]>x do inc(i);      while d[j]<x do dec(j);      if i<=j then      begin         y:=d[i]; d[i]:=d[j]; d[j]:=y;         z:=c[i]; c[i]:=c[j]; c[j]:=z;         inc(i); dec(j);      end;   end;   if i<r then sort(i,r);   if j>l then sort(l,j);end;begin   read(n,k);   while (n<>0) do   begin      for i:=1 to n do read(a[i]);      for i:=1 to n do read(b[i]);      m:=n-k;      l:=1;      repeat         ans:=l;         for i:=1 to n do         begin             d[i]:=a[i]-l*b[i]; c[i]:=i;         end;         sort(1,n);         p:=0; q:=0;         for i:=1 to m do         begin            inc(p,a[c[i]]);            inc(q,b[c[i]]);         end;         l:=p/q;      until abs(l-ans)<eps;      writeln(round(ans*100));      read(n,k);   end;end.


poj 2728

最优比率生成树,题目要求是比率最小,题目的限制是树,所以我们用prim来记录我们选择的边(这种图真的不建议用kruscal...),Dinkelbach算法会比二分快,所以这里就只写了Dinkelbach算法,注意每次都要更新a、b、d数组

const        eps=1e-6;var        n,pos,ta        :longint;        l,ans,p,q,tt    :double;        tb,td           :double;        i,j             :longint;        x,y,z,a         :array[0..1010] of longint;        flag            :array[0..1010] of boolean;        b,d             :array[0..1010] of double;begin   read(n);   while (n<>0) do   begin      for i:=1 to n do read(x[i],y[i],z[i]);      l:=0;      repeat          ans:=l; p:=0; q:=0;          for i:=2 to n do          begin             a[i]:=abs(z[i]-z[1]);             b[i]:=sqrt(sqr(x[i]-x[1])+sqr(y[i]-y[1]));             d[i]:=a[i]-l*b[i];          end;          fillchar(flag,sizeof(flag),false);          flag[1]:=true;          for i:=1 to n-1 do          begin             tt:=1e9;             for j:=2 to n do               if not flag[j] and (d[j]<tt) then               begin                  pos:=j; tt:=d[j];               end;             flag[pos]:=true;             p:=p+a[pos]; q:=q+b[pos];             for j:=2 to n do               if not flag[j] then               begin                  ta:=abs(z[j]-z[pos]);                  tb:=sqrt(sqr(x[j]-x[pos])+sqr(y[j]-y[pos]));                  td:=ta-l*tb;                  if td<d[j] then                  begin                     d[j]:=td; a[j]:=ta; b[j]:=tb;                  end;               end;          end;          l:=p/q;      until abs(l-ans)<eps;      writeln(ans:0:3);      read(n);   end;end.



poj 3621

最优比率环,题目要求比率最大,没啥限制,只要存在正环就说明还有更优的答案。将边建成负的,这样判正环就变成了判负环,由于数据范围并不是很大,所以就用了Bellman_ford。这类题只用二分

const        eps=1e-6;var        n,m             :longint;        l,r,mid         :double;        i               :longint;        a               :array[0..1010] of longint;        x,y,b           :array[0..5010] of longint;        d               :array[0..5010] of double;        dis             :array[0..1010] of double;function bellman_ford(l:double):boolean;var        i,j:longint;        flag:boolean;begin   for i:=1 to m do d[i]:=-(a[x[i]]-l*b[i]);   for i:=1 to n do dis[i]:=0;   for i:=1 to n do   begin      flag:=false;      for j:=1 to m do        if (dis[x[j]]+d[j]<dis[y[j]]) then        begin           dis[y[j]]:=dis[x[j]]+d[j];           flag:=true;        end;      if not flag then exit(false);   end;   exit(true);end;begin   read(n,m);   for i:=1 to n do read(a[i]);   for i:=1 to m do read(x[i],y[i],b[i]);   //   l:=0; r:=20000;   while (r-l>=eps) do   begin      mid:=(l+r)/2;      if bellman_ford(mid) then l:=mid else r:=mid;   end;   if l>eps then writeln(l:0:2) else writeln(0);end.


bzoj 1486

最优比率环,题目要求比率最小,也没啥其他限制。这次就直接建边判负环就行了,这数据范围相当卡,所以用dfs版spfa判负环...

const        eps=1e-9;var        n,m,ll,x,y,z    :longint;        i               :longint;        l,r,mid         :double;        vis             :array[0..3010] of boolean;        last            :array[0..3010] of longint;        pre,other,len   :array[0..10010] of longint;        dis             :array[0..3010] of double;        flag            :boolean;procedure connect(x,y,z:longint);begin   inc(ll);   pre[ll]:=last[x];   last[x]:=ll;   other[ll]:=y;   len[ll]:=z;end;procedure dfs(x:longint;l:double);var        p,q:longint;begin   if flag then exit;   vis[x]:=true;   q:=last[x];   while (q<>0) do   begin      if flag then exit;      p:=other[q];      if dis[p]>dis[x]+len[q]-l then      begin         dis[p]:=dis[x]+len[q]-l;         if vis[p] then         begin            flag:=true;exit;         end else dfs(p,l);      end;      q:=pre[q];   end;   vis[x]:=false;end;function check(x:double):boolean;var        i:longint;begin   flag:=false;   for i:=1 to n do dis[i]:=0;   for i:=1 to n do vis[i]:=false;   for i:=1 to n do   begin      dfs(i,x);      if flag then exit(true);   end;   exit(false);end;begin   read(n,m);   for i:=1 to m do   begin      read(x,y,z);      connect(x,y,z);   end;   l:=0; r:=1e7;   while (r-l>=eps) do   begin      mid:=(l+r)/2;      if check(mid) then r:=mid else l:=mid;   end;   writeln(r:0:8);end.
——by Eirlys



0 0
原创粉丝点击