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

61 篇文章 0 订阅
2 篇文章 0 订阅

关于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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值