01分数规划讲解

  分数规划是将某个求解最优性问题转化为判定性问题,一般的形式为f(x)=a(x)/b=(x),求解f(x)的最优值,其中a,b,x为连续实数函数。

  我们这里讨论f的最小值,即设w=min(f(x)=a(x)/b(x)),那么w=f(xmin)=a(xmin)/b(xmin)=>a(xmin)-w*b(xmin)=0

  那么我们设一个新的函数g(w),g(w)=min(a(x)-w*b(x)),首先我们可以得到这个函数的一些性质。

性质:

  单减性:即对于任意w1<w2都有g(w1)>g(w2),证明比较容易,假设x1为w1的最优解,x2为w2的最优解,那么有

    g(w1)=a(x1)-w1*b(x1)>a(x1)-w2*b(x1)>=a(x2)-w2*b(x2)=g(w2)

  唯一性:当且仅当g(w)=0时,w为原问题的最优解。这一性质又被称作Dinkelbach定理。首先我们要证明这一性质,需要证明g(w)=0是w为最优解的充分必要条件。

    首先证明必要性,即对于w为最优解时,g(w)=0。设最优解为wmin,那么我们有

    wmin=a(xmin)/b(xmin)<=a(x)/b(x),也就是a(x)-wmin*b(x)>=0,因为g(w)是取最小值,所以g(wmin)=0

    然后证明充分性,即对于g(w)=0时,w为原文题的最优解。

    采用反证法,假设w’比w更优,那么w’=a(x')/b(x')<w,那么a(x')-w*b(x')<0,也就是g(w)<0,与题设g(w)=0不符。

这时候,我们可以通过求出来g(w)的值判断w是否为最优解,这样就将原问题的最优性问题转换为了判定性问题。

比如bzoj3232 http://61.187.179.132/JudgeOnline/problem.php?id=3232

这道题要求求v/c的最优值,这样不容易求解,但是我们可以用网络流最小割求出v-w*c的最小值,然后判断与0的关系,这样二分求解。

//By BLADEVIL

const

    lim                         =1e-5;

      

var

    n, m                        :longint;

    pre, other                  :array[0..100010] of longint;

    len                         :array[0..100010] of extended;

    last                        :array[0..3010] of longint;

    tot                         :longint;

    num                         :array[0..60,0..60] of longint;

    key, heng, shu              :array[0..60,0..60] of longint;

    sum                         :longint;

    print                       :extended;

    que, d                      :array[0..3010] of longint;

    source, sink                :longint;

      

function min(a,b:extended):extended;

begin

    if a>b then min:=b else min:=a;

end;

  

function judge(x:extended):extended;

begin

    if abs(x)<lim then exit(0);

    if x<0 then exit(-1) else exit(1);

end;

      

procedure connect(x,y:longint;z:extended);

begin

    inc(tot);

    pre[tot]:=last[x];

    last[x]:=tot;

    other[tot]:=y;

    len[tot]:=z;

end;

      

procedure init;

var

    i, j                        :longint;

      

begin

    read(n,m);

    for i:=1 to n do

        for j:=1 to m do num[i,j]:=(i-1)*m+j;

    for i:=1 to n do

        for j:=1 to m do

        begin

            read(key[i,j]);

            sum:=sum+key[i,j];

        end;

    for i:=1 to n+1 do

        for j:=1 to m do read(heng[i,j]);

    for i:=1 to n do

        for j:=1 to m+1 do read(shu[i,j]);

    source:=num[n,m]+2;

    sink:=source+1;

end;

  

function bfs:boolean;

var

    q, p                        :longint;

    h, t, cur                   :longint;

begin

    fillchar(d,sizeof(d),0);

    d[source]:=1;

    h:=0; t:=1; que[1]:=source;

    while h<t do

    begin

        inc(h);

        cur:=que[h];

        q:=last[cur];

        while q<>0 do

        begin

            p:=other[q];

            if (judge(len[q])>0) and (d[p]=0) then

            begin

                inc(t);

                que[t]:=p;

                d[p]:=d[cur]+1;

                if p=sink then exit(true);

            end;

            q:=pre[q];

        end;

    end;

    exit(false);

end;

  

function dinic(x:longint;flow:extended):extended;

var

    rest, tmp                   :extended;

    q, p                        :longint;

      

begin

    if x=sink then exit(flow);

    rest:=flow;

    q:=last[x];

    while q<>0 do

    begin

        p:=other[q];

        if (judge(len[q])>0) and (d[p]=d[x]+1) and (rest>0) then

        begin

            tmp:=dinic(p,min(rest,len[q]));

            rest:=rest-tmp;

            len[q]:=len[q]-tmp;

            len[q xor 1]:=len[q xor 1]+tmp;

        end;

        q:=pre[q];

    end;

    exit(flow-rest);

end;

  

procedure main;

var

    l, r, mid                   :extended;

    cur                         :longint;

    ans                         :extended;

    i, j                        :longint;

      

begin

    l:=0; r:=90;

    while r-l>lim do

    begin

        mid:=(l+r)/2;

        fillchar(last,sizeof(last),0);

        tot:=1;

        for i:=1 to n do

            for j:=1 to m do

            begin

                connect(source,num[i,j],key[i,j]);

                connect(num[i,j],source,0);

            end;

          

        for i:=1 to n do

            for j:=1 to m do

            begin

                cur:=0;

                if i=1 then inc(cur,heng[i,j]);

                if i=n then inc(cur,heng[i+1,j]);

                if j=1 then inc(cur,shu[i,j]);

                if j=m then inc(cur,shu[i,j+1]);

                if cur>0 then

                begin

                    connect(num[i,j],sink,cur*mid);

                    connect(sink,num[i,j],0);

                end;

            end;

        for i:=1 to n-1 do

            for j:=1 to m do

            begin

                connect(num[i,j],num[i+1,j],heng[i+1,j]*mid);

                connect(num[i+1,j],num[i,j],heng[i+1,j]*mid);

            end;

        for i:=1 to n do

            for j:=1 to m-1 do

            begin

                connect(num[i,j],num[i,j+1],shu[i,j+1]*mid);

                connect(num[i,j+1],num[i,j],shu[i,j+1]*mid);

            end;

        ans:=0;

        while bfs do

            ans:=ans+dinic(source,maxlongint);

        if judge(sum-ans)>0 then l:=mid else r:=mid;

    end;

    writeln(l:0:3);

end;

  

begin

    init;

    main;

end.

 

你可能感兴趣的:(规划)