zoukankan      html  css  js  c++  java
  • bzoj 2668 费用流

      我们可以把初始状态转化为目标状态这一约束转化为将黑子移动到目标状态所需要的最少步数。

      除了初始点和目标点之外,剩下的点如果被经过那么就会被交换两次,所以我们将一个点拆成3个点,a,b,c,新建附加源点source汇点sink。设每个点的交换次数为num[i],那么这个点的交换次数如果是奇数其实是没用的,那么我们连接(a,b,num[i] div 2,0),(b,c,num[i] div 2,0),这样代表这个点一共可以被经过num[i] div 2次,那么对于起始点和终点来说,因为不用被经过,所以原来是奇数的话可以走num[i] div 2+1次,所以(a,b,(num[i]+1) div 2,0),(b,c,(num[i]+1) div 2)。对于两个相邻的点(八连通)连接(ci,aj,inf,1)代表随意经过(每个点的经过次数在拆的点内被限制了,这里不用限制)1次需要1的代价。再连接每个初始状态上的黑点(source,b,1,0),连接每个目标状态上的黑点(b,sink,1,0),代表出发点。

      需要注意的细节是如果一个点目标状态和初始状态相同,那么会出现这样一种情况:source连接b,b连接sink,然后着两条边可以相当于没有,但是a连b,b连c的流量是(num[i]+1) div 2。这代表原来+1是因为到这个点之后停止,不需要再流出(经过),所以奇数的话可以多走一次。现在这个点因为源汇的直接连接使得这个点和普通路径上的点相同,那么就不应该+1,所以我们可以处理初始状态和目标状态上的相同的点,直接不考虑,连接的流量也是(num[i]) div 2。还有一种处理方法就是对于初始状态上的点连接(a,b,num[i] div 2,0),(b,c,(num[i]+1) div 2)目标状态上的点连接(a,b,(num[i]+1) div 2,0),(b,c,num[i] div 2)分别代表可以多进入(出去)一次,这一次是直接从source(sink)流入(流出)的。这两种方法都可以解决这一问题。

      对于无解,有两种情况,第一个是初始状态目标状态上的黑点个数不同,第二种情况是没有达到满流,代表不是所有点都流到目标状态,特判就行了。

    /**************************************************************
        Problem: 2668
        User: BLADEVIL
        Language: Pascal
        Result: Accepted
        Time:48 ms
        Memory:6516 kb
    ****************************************************************/
     
    //By BLADEVIL
    var
        n, m                        :longint;
        num                         :array[0..30,0..30] of longint;
        source, sink                :longint;
        go                          :array[0..2,0..8] of longint;
        ans                         :longint;
        last                        :array[0..2010] of longint;
        pre, other, len, cost       :array[0..400010] of longint;
        l                           :longint;
        que, d, father              :array[0..2010] of longint;
        flag                        :array[0..2010] of boolean;
        map                         :array[0..30,0..30] of longint;
         
    function min(a,b:longint):longint;
    begin
        if a>b then min:=b else min:=a;
    end;
         
    procedure connect(a,b,c,d:longint);
    begin
        inc(l);
        pre[l]:=last[a];
        last[a]:=l;
        other[l]:=b;
        len[l]:=c;
        cost[l]:=d;
    end;
         
    procedure init;
    var
        i, j, k                     :longint;
        s                           :char;
        sum1, sum2                  :longint;
        key                         :longint;
        nx, ny                      :longint;
         
    begin
        readln(n,m);
        go[1,1]:=-1; go[2,2]:=1; go[1,3]:=1; go[2,4]:=-1;
        go[1,5]:=-1; go[2,5]:=1;
        go[1,6]:=1; go[2,6]:=1;
        go[1,7]:=1; go[2,7]:=-1;
        go[1,8]:=-1; go[2,8]:=-1;
        sum1:=0; sum2:=0; l:=1;
        for i:=1 to n do
            for j:=1 to m do num[i,j]:=(i-1)*m+j;
         
        source:=n*m*3+2; sink:=source+1;
        for i:=1 to n do
        begin
            for j:=1 to m do
            begin
                read(s);
                if s='1' then
                begin
                    connect(source,num[i,j]+n*m,1,0);
                    connect(num[i,j]+n*m,source,0,0);
                    map[i,j]:=1;
                    inc(sum1);
                end;
            end;
            readln;
        end;
        for i:=1 to n do
        begin
            for j:=1 to m do
            begin  
                read(s);
                if s='1' then
                begin
                    connect(num[i,j]+n*m,sink,1,0);
                    connect(sink,num[i,j]+n*m,0,0);
                    map[i,j]:=2;
                    inc(sum2);
                end;
            end;
            readln;
        end;
        if sum1<>sum2 then
        begin
            writeln(-1);
            halt;
        end;
        for i:=1 to n do
        begin
            for j:=1 to m do
            begin
                read(s);
                key:=ord(s)-48;
                if map[i,j]=0 then
                begin
                    connect(num[i,j],num[i,j]+n*m,key div 2,0);
                    connect(num[i,j]+n*m,num[i,j],0,0);
                    connect(num[i,j]+n*m,num[i,j]+2*n*m,key div 2,0);
                    connect(num[i,j]+2*n*m,num[i,j]+n*m,0,0);
                end else
                if map[i,j]=1 then
                begin
                    connect(num[i,j],num[i,j]+n*m,key div 2,0);
                    connect(num[i,j]+n*m,num[i,j],0,0);
                    connect(num[i,j]+n*m,num[i,j]+2*n*m,(key+1) div 2,0);
                    connect(num[i,j]+2*n*m,num[i,j]+n*m,0,0);
                end else
                begin
                    connect(num[i,j],num[i,j]+n*m,(key+1) div 2,0);
                    connect(num[i,j]+n*m,num[i,j],0,0);
                    connect(num[i,j]+n*m,num[i,j]+2*n*m,key div 2,0);
                    connect(num[i,j]+2*n*m,num[i,j]+n*m,0,0);
                end;
            end;
            readln;
        end;
        for i:=1 to n do
            for j:=1 to m do
                for k:=1 to 8 do
                begin
                    nx:=i+go[1,k];
                    ny:=j+go[2,k];
                    if (nx<1) or (nx>n) or (ny<1) or (ny>m) then continue;
                    connect(num[i,j]+2*n*m,num[nx,ny],maxlongint div 10,1);
                    connect(num[nx,ny],num[i,j]+2*m*n,0,-1);
                end;
    end;
     
    function spfa:boolean;
    var
        cur, h, t                   :longint;
        q, p                        :longint;
    begin
        filldword(d,sizeof(d) div 4, maxlongint div 10);
        d[source]:=0; 
        h:=0; t:=1;
        que[1]:=source;
        while h<>t do
        begin
            h:=h mod 2000+1;
            cur:=que[h];
            flag[cur]:=false;
            q:=last[cur];
            while q<>0 do
            begin
                if len[q]>0 then
                begin
                    p:=other[q];
                    if d[p]>d[cur]+cost[q] then
                    begin
                        d[p]:=d[cur]+cost[q];
                        father[p]:=q;
                        if not flag[p] then
                        begin
                            t:=t mod 2000+1;
                            que[t]:=p;
                            flag[p]:=true;
                        end;
                    end;
                end;
                q:=pre[q];
            end;
        end;
        if d[sink]=maxlongint div 10 then exit(false) else exit(true);
    end;
     
    procedure update;
    var
        low, cur                    :longint;
    begin
        cur:=sink;
        low:=maxlongint;
        while cur<>source do
        begin
            low:=min(low,len[father[cur]]);
            cur:=other[father[cur] xor 1];
        end;
        cur:=sink;
        while cur<>source do
        begin
            dec(len[father[cur]],low);
            inc(len[father[cur] xor 1],low);
            inc(ans,low*cost[father[cur]]);
            cur:=other[father[cur] xor 1];
        end;
    end;
     
    procedure main;
    var
        q                           :longint;
    begin
        while spfa do update;
        q:=last[source];
        while q<>0 do
        begin
            if len[q]>0 then
            begin
                writeln(-1);
                halt;
            end;
            q:=pre[q];
        end;
        writeln(ans);
    end;
     
    begin
        init;
        main;
    end.
  • 相关阅读:
    datatable一些用法续
    验证视图状态MAC失败
    datatable应用select方法后变成行数组的问题的解决
    [jQuery] html中两个select之间option元素的add与remove,多值上传
    [VBS] 使用vbs文件保证程序运行,并模拟键盘回车键
    [jQuery] 为table各行添加不同颜色的class
    [转]谈谈个人网站建设和经营
    [jQuery] jquery如何reset表单(form)
    兼容Firefox IE Chrome的onkeydown事件处理方法
    [bat] 使用bat文件保证指定程序运行
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3511603.html
Copyright © 2011-2022 走看看