现在的位置: 首页 > 综合 > 正文

bzoj2668

2018年01月15日 ⁄ 综合 ⁄ 共 2441字 ⁄ 字号 评论关闭

Description

有一个nm列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

Input

第一行包含两个整数nm(1<=n,
m
<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。
 

Output

输出仅一行,为最小交换总次数。如果无解,输出-1。

Sample Input

3 3
110
000
001
000
110
100
222
222
222

Sample Output

4

网络流

明显格子等价于可以互相穿过,棋子是一样的

原本就在位置上的棋子我们没必要移

program bzoj2668;
const
    zl:array [1..8,1..2] of longint=((1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1));
var
	tot,u,v,x,y,count,ans,n,m,i,j,k:longint;
    lim:array [0..21,0..21] of longint;
    a,b:array [0..21,0..21] of char;
    last,dis,root:array [0..801] of longint;
    dl,point,next,cost,flow:array [0..100001] of longint;
    ch:char;

function anti (now:longint):longint;inline;
begin
    if now and 1 = 1 then exit(now+1)
                            else exit(now-1);
end;

procedure connect (u,v,f,c:longint);inline;
begin
    inc(tot);
    point[tot]:=v;
    flow[tot]:=f;
    cost[tot]:=c;
    next[tot]:=root[u];
    root[u]:=tot;
end;

procedure mcmf;
begin
    repeat
        fillchar(dis,sizeof(dis),63);
        tot:=0; 
        dl[0]:=0;
        dis[0]:=0;
        i:=0;
        while i<=tot do
            begin
                k:=root[dl[i]];
                while k<>0 do
                    begin
                        if (flow[k]>0)and(dis[point[k]]>dis[dl[i]]+cost[k]) then
                            begin
                                last[point[k]]:=k;
                                dis[point[k]]:=dis[dl[i]]+cost[k];
                                inc(tot);
                                dl[tot]:=point[k];
                            end;
                        k:=next[k];
                    end;
                inc(i);
            end;
        if dis[801]>1000000 then break; 
        dec(count);
        ans:=ans+dis[801];
        i:=801;
        while i<>0 do
            begin
                dec(flow[last[i]]);
                inc(flow[anti(last[i])]);
                i:=point[anti(last[i])];
            end;
    until false;
end;

begin
    readln(n,m);
    count:=0;
    for i:=1 to n do
        begin
            for j:=1 to m do
                begin
                    read(a[i,j]);
                    if a[i,j]='1' then inc(count);
                end;
            readln;
        end;
    k:=0;
    for i:=1 to n do
        begin
            for j:=1 to m do
                begin
                    read(b[i,j]);
                    if b[i,j]='1' then inc(k);
                end;
            readln;
        end;
    if count<>k then
        begin
            writeln(-1);
            exit;
        end;
    for i:=1 to n do
        begin
            for j:=1 to m do
                begin
                    read(ch);
                    lim[i,j]:=ord(ch)-48;
                end;
            readln;
        end;
    for i:=1 to n do
        for j:=1 to m do
            if (a[i,j]='1')and(b[i,j]='1') then
                begin
                    dec(count);
                    k:=i*m-m+j;
                    connect(k*2-1,k*2,lim[i,j] div 2,0);
                    connect(k*2,k*2-1,0,0);
                end
                                                    else
            if (a[i,j]='1')or(b[i,j]='1') then
                begin
                    k:=i*m-m+j;
                    if lim[i,j]=0 then
                        begin
                            writeln(-1);
                            exit;
                        end;
                    connect(k*2-1,k*2,(lim[i,j]-1) div 2 + 1,0);
                    connect(k*2,k*2-1,0,0);
                    if a[i,j]='1' then
                        begin
                            connect(0,k*2-1,1,0);
                            connect(k*2-1,0,0,0);
                        end;
                    if b[i,j]='1' then
                        begin
                            connect(k*2,801,1,0);
                            connect(801,k*2,0,0);
                        end;
                end
                                                    else
                begin
                    k:=i*m-m+j;
                    connect(k*2-1,k*2,lim[i,j] div 2,0);
                    connect(k*2,k*2-1,0,0);
                end;
    for i:=1 to n do
        for j:=1 to m do
            for k:=1 to 8 do
                begin
                    x:=i+zl[k,1];
                    y:=j+zl[k,2];
                    if (x<1)or(x>n)or(y<1)or(y>m) then continue;
                    u:=i*m-m+j;
                    v:=x*m-m+y;
                    connect(u*2,v*2-1,maxlongint div 10,1);
                    connect(v*2-1,u*2,0,-1);
                end;
    mcmf;
    if count<>0 then
        begin
            writeln(-1);
            exit;
        end;
    writeln(ans);
end.
【上篇】
【下篇】

抱歉!评论已关闭.