bzoj 3232 01分数规划

题意:n*m的方格,每个方格有一个收益,每一条单位长度的方格线都一定费用,从任意格点出发,沿着格线行走至到出发点,收益为形成的封闭图形内的方格的收益,总费用为走的格线的费用和,求 总收益/总费用 的最大值

第一眼显然01分数规划...,需要想办法维护形成的封闭图形的总收益

我们默认他是逆时针走的

这样,利用差分的思想,我们把从下往上走的边的收益定义为这一行它左边的收益和,把从左往右走的的收益定义为这一列它上边的权益和,把从上往下走的收益定义为这一行它左边的收益和的相反数,把从右往左的收益定义为这一列它上边的权益和的相反数,这样就可以跑01分数规划了,最后把答案除以2就行了

const
        eps=1e-6;

var
        n,m,x,l         :longint;
        i,j             :longint;
        flag            :boolean;
        ll,r,mid        :double;
        a,b,pre,other   :array[0..11010] of longint;
        last            :array[0..3605] of longint;
        num,sl,sr       :array[0..55,0..55] of longint;
        vis             :array[0..3605] of boolean;
        d               :array[0..11010] of double;
        dis             :array[0..3605] of double;

procedure connect(x,y,aa,bb:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   a[l]:=aa;
   b[l]:=bb;
end;

procedure dfs(x:longint);
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]+d[q] then
      begin
         dis[p]:=dis[x]+d[q];
         if vis[p] then
         begin
            flag:=true; exit;
         end else dfs(p);
      end;
      q:=pre[q];
   end;
   vis[x]:=false;
end;

function check(ll:double):boolean;
var
        i:longint;
begin
   flag:=false;
   fillchar(dis,sizeof(dis),0);
   fillchar(vis,sizeof(vis),false);
   for i:=1 to l do d[i]:=-(a[i]-ll*b[i]);
   for i:=1 to (n+1)*(m+1) do
   begin
      dfs(i);
      if flag then exit(true);
   end;
   exit(false);
end;

begin
   read(n,m);
   for i:=1 to n do
     for j:=1 to m do
     begin
        read(x);
        sl[i,j]:=sl[i,j-1]+x;
        sr[i,j]:=sr[i-1,j]+x;
     end;
   //
   for i:=1 to n+1 do
     for j:=1 to m+1 do num[i,j]:=(i-1)*(m+1)+j;
   //
   for i:=1 to n+1 do
     for j:=1 to m do
     begin
        read(x);
        connect(num[i,j],num[i,j+1],sr[i-1,j],x);
        connect(num[i,j+1],num[i,j],-sr[i-1,j],x);
     end;
   //
   for i:=1 to n do
     for j:=1 to m+1 do
     begin
        read(x);
        connect(num[i+1,j],num[i,j],sl[i,j-1],x);
        connect(num[i,j],num[i+1,j],-sl[i,j-1],x);
     end;
   //
   ll:=0; r:=1e6;
   while (r-ll>=eps) do
   begin
      mid:=(ll+r)/2;
      if check(mid) then ll:=mid else r:=mid;
   end;
   ll:=ll/2; 
   writeln(ll:0:3);
end.
——by Eirlys


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值