bzoj2668

对于这种题很容易看出是费用流吧……

但这道题不容易建模;

首先是怎么表示目标状态和其实状态,看起来有黑有白很复杂

但实际上,不难发现,白色格子没什么用,起决定作用的是黑格子

也就是我们可以把问题简化:我们怎么把开始的黑格子移到目标位置

但这个移动不是一般的移动;

在一条路径中,不难发现,起始两点是只要交换一次,其他路径上个点都是要交换2次

由于题目给出的限制是点的交换次数限制c,所以不难想到要拆点

但是平常的拆两点好像无法表示这个特征,

于是我们就拆成3个点……

    p1--->p0--->p2

p1表示交换进来,p2表示交换出去;

所以不难得出:

对于每个点,如果它是原图中的黑点,连边<p1,p0,c/2,0>,<p0,p2,(c+1)/2,0>,<s,p0,1,0>;

如果它是新图中的黑点,连边<p1,p0,(c+1)/2>,<p0,p2,c/2,0>,<p0,t,1,0>;

注意存在有的点在新图原图都是黑点的情况(在这里WA了一次)

如果它在两个图中都是白点,那么连边<p1,p0,c/2,0>,<p0,p2,c/2,0>

最后对于原图中任意可达两点,连边<pi2,pj1,inf,1>

注意这道题可以交换对角线,还要判断是否可行,细节挺多

  1 const dx:array[1..8] of integer=(-1,1,0,0,1,-1,-1,1);
  2       dy:array[1..8] of integer=(0,0,1,-1,1,-1,1,-1);
  3       inf=10000007;
  4 type node=record
  5        next,point,cost,flow:longint;
  6      end;
  7 
  8 var edge:array[0..1000010] of node;
  9     p,pre,cur,d:array[0..2010] of longint;
 10     v:array[0..2010] of boolean;
 11     a,b,c:array[0..30,0..30] of integer;
 12     q:array[0..1001000] of longint;
 13     ch,t,n,m,i,j,k,x,y,po,tot,sum,ans,len:longint;
 14     s:string;
 15 
 16 procedure add(x,y,f,w:longint);
 17   begin
 18     inc(len);
 19     edge[len].point:=y;
 20     edge[len].flow:=f;
 21     edge[len].cost:=w;
 22     edge[len].next:=p[x];
 23     p[x]:=len;
 24   end;
 25 
 26 function spfa:boolean;
 27   var i,x,y,f,r:longint;
 28   begin
 29     fillchar(v,sizeof(v),false);
 30     v[0]:=true;
 31     for i:=1 to t do
 32       d[i]:=inf;
 33     d[0]:=0;
 34     f:=1;
 35     r:=1;
 36     q[f]:=0;
 37     while f<=r do
 38     begin
 39       x:=q[f];
 40       v[x]:=false;
 41       i:=p[x];
 42       while i<>-1 do
 43       begin
 44         y:=edge[i].point;
 45         if edge[i].flow>0 then
 46           if d[y]>d[x]+edge[i].cost then
 47           begin
 48             d[y]:=d[x]+edge[i].cost;
 49             pre[y]:=x;
 50             cur[y]:=i;
 51             if not v[y] then
 52             begin
 53               inc(r);
 54               q[r]:=y;
 55               v[y]:=true;
 56             end;
 57           end;
 58         i:=edge[i].next;
 59       end;
 60       inc(f);
 61     end;
 62     if d[t]=inf then exit(false) else exit(true);
 63   end;
 64 
 65 procedure mincost;
 66   var i,j,neck:longint;
 67   begin
 68     while spfa do
 69     begin
 70       i:=t;
 71       neck:=inf;
 72       while i<>0 do
 73       begin
 74         j:=cur[i];
 75         if neck>edge[j].flow then neck:=edge[j].flow;
 76         i:=pre[i];
 77       end;
 78       i:=t;
 79       while i<>0 do
 80       begin
 81         j:=cur[i];
 82         dec(edge[j].flow,neck);
 83         inc(edge[j xor 1].flow,neck);
 84         i:=pre[i];
 85       end;
 86       ans:=ans+d[t]*neck;
 87       dec(ch,neck);
 88       if ch=0 then break;
 89     end;
 90   end;
 91 
 92 begin
 93   readln(n,m);
 94   len:=-1;
 95   fillchar(p,sizeof(p),255);
 96   t:=3*n*m+1;
 97   for i:=1 to n do
 98   begin
 99     readln(s);
100     for j:=1 to m do
101     begin
102       a[i,j]:=ord(s[j])-48;
103       tot:=tot+a[i,j];
104     end;
105   end;
106   for i:=1 to n do
107   begin
108     readln(s);
109     for j:=1 to m do
110     begin
111       b[i,j]:=ord(s[j])-48;
112       sum:=sum+b[i,j];
113     end;
114   end;
115   for i:=1 to n do
116   begin
117     readln(s);
118     for j:=1 to m do
119       c[i,j]:=ord(s[j])-48;
120   end;
121   if sum<>tot then
122   begin
123     writeln(-1);
124     halt;
125   end
126   else ch:=sum;
127   sum:=n*m;
128   for i:=1 to n do
129   begin
130     for j:=1 to m do
131     begin
132       x:=(i-1)*m+j;
133       if a[i,j]=1 then
134       begin
135         add(x+sum,x,c[i,j] shr 1,0);
136         add(x,x+sum,0,0);
137         add(x,x+2*sum,(c[i,j]+1) shr 1,0);
138         add(x+2*sum,x,0,0);
139         add(0,x,1,0);
140         add(x,0,0,0);
141         if b[i,j]=1 then    //细节
142         begin
143           add(x,t,1,0);
144           add(t,x,0,0);
145         end;
146       end
147       else if b[i,j]=1 then
148       begin
149         add(x+sum,x,(c[i,j]+1) shr 1,0);
150         add(x,x+sum,0,0);
151         add(x,x+2*sum,c[i,j] shr 1,0);
152         add(x+2*sum,x,0,0);
153         add(x,t,1,0);
154         add(t,x,0,0);
155       end
156       else if (a[i,j]+b[i,j]=0) then
157       begin
158         add(x+sum,x,c[i,j] shr 1,0);
159         add(x,x+sum,0,0);
160         add(x,x+2*sum,c[i,j] shr 1,0);
161         add(x+2*sum,x,0,0);
162       end;
163     end;
164   end;
165 
166   for i:=1 to n do
167     for j:=1 to m do
168     begin
169       po:=(i-1)*m+j;
170       for k:=1 to 8 do
171       begin
172         x:=i+dx[k];
173         y:=j+dy[k];
174         if (x<=n) and (y<=m) and (x>0) and (y>0) then
175         begin
176           tot:=(x-1)*m+y;
177           add(po+2*sum,tot+sum,inf,1);
178           add(tot+sum,po+2*sum,0,-1);
179         end;
180       end;
181     end;
182   mincost;
183   if ch<>0 then writeln(-1) else writeln(ans);
184 end.
View Code

 

转载于:https://www.cnblogs.com/phile/p/4473249.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值