题目描述
一些蛇覆盖了一个网格。每个格子要么是一个障碍物,要么是蛇的一部分。每条蛇
占据了一条折线(拐角处只能水平和竖直连接),且至少占据两个格子。蛇与蛇之间不
重叠,蛇也不会与自己重叠。每条蛇还必须满足以下两个条件中的一个:
1. 两个端点所在的格子在网格的边界。
2. 蛇构成一个环,即两个端点相邻(垂直或水平,不能斜着),至少要占据4个
格子(否则没法形成环)。
给定一个网格,用? × ?的字符矩阵描述:’#’代表障碍物,’.’代表空地。在满足
所述的条件下覆盖所有空地,并使得端点在网格边界(即不构成环)的蛇尽量少。
例如,以下网格:
......
.#.##.
.#....
....#.
.##.#.
......
给定一个网格的描述,输出最少需要多少条不构成环的蛇来覆盖这个网格。如果不
存在能够覆盖网格的方案,输出-1。
一个字符矩阵,行数和列数不超过12
一看数据规模,一看矩阵,立马想到基于连通性的状压dp
但这道题实际上是可以用网络流来解(考场上一直往dp上想的说)
出题人的费用流做法就不赘述了,这里讲一讲高二学长梁盾的最大流做法
将图红蓝染色,每个点若被snake覆盖,如果不是端点,必度为2,若是端点,度为1。
源点向红点(非边界)连上下界均为二,表示至少经过2次,边界点连下界为1,上界为2的边,表示可以是起点,也可以是中间点。
蓝点向汇点连同理。
红点蓝点若相邻,且无障碍,连下界为0,上界为1,表示是可以到达。
跑上下界最大流,无可行流即无解。
与源汇点相连的流量为1的点数量即为端点数,div 2即为蛇的数量。
const max=1073741819;
var s,t,ps,pt,ss,s1,l:longint;
n:array[1..12]of string;
tail,d,st,chu,ru:array[0..150]of longint;
p:array[0..150]of boolean;
next,sora,flow,pow:array[0..100000]of longint;
tot,ans:longint;
function bfs(s,t:longint):boolean;
var h,r,i,ne,na:longint;
begin
fillchar(st,sizeof(st),0);fillchar(d,sizeof(d),127);
h:=0;r:=1;st[1]:=s;d[s]:=0;
repeat
inc(h);ne:=st[h];
i:=ne;
while next[i]<>0 do begin
i:=next[i];na:=sora[i];
if (pow[i]<>0)and(flow[i]>0)and(d[ne]+1<d[na]) then begin
d[na]:=d[ne]+1;
inc(r);st[r]:=na
end
end
until h>=r;
if d[t]<max then exit(true);
exit(false)
end;
function dfs(x,low,t:longint):longint;
var i,ne,tmp:longint;
begin
if x=t then exit(low);
dfs:=0;i:=x;
while next[i]<>0 do begin
i:=next[i];ne:=sora[i];
if (pow[i]<>0)and(flow[i]>0)and(d[x]+1=d[ne]) then begin
if flow[i]<low then tmp:=dfs(ne,flow[i],t)
else tmp:=dfs(ne,low,t);
if tmp=0 then d[ne]:=max;
dec(flow[i],tmp);dec(low,tmp);inc(flow[pow[i]],tmp);inc(dfs,tmp);
if low=0 then break
end
end
end;
procedure origin;
var i:longint;
begin
s:=0;t:=l*s1+1;ps:=t+1;pt:=ps+1;
for i:=s to pt do tail[i]:=i;
ss:=pt
end;
procedure link1(x,y,z:longint);
begin
inc(ss);next[tail[x]]:=ss;tail[x]:=ss;sora[ss]:=y;flow[ss]:=z;
inc(ss);next[tail[y]]:=ss;tail[y]:=ss;sora[ss]:=x;flow[ss]:=0;
pow[ss]:=ss-1;pow[ss-1]:=ss;
if x=ps then inc(tot,z)
end;
procedure link(x,y,l,u:longint);
begin
chu[x]:=chu[x]+l;ru[y]:=ru[y]+l;
if u<>l then link1(x,y,u-l)
end;
procedure init;
var i,j,x:longint;
begin
fillchar(n,sizeof(n),0);s1:=0;
while not(seekeof) do begin
inc(s1);readln(n[s1])
end;
l:=length(n[1]);
origin;
fillchar(p,sizeof(p),false);
p[s]:=true;p[t]:=true;
for i:=1 to s1 do
for j:=1 to l do
if n[i,j]='.' then begin
x:=(i-1)*l+j;p[x]:=true;
if (i+j) and 1=0 then begin
if (i=1)or(i=s1)or(j=1)or(j=l) then link(s,x,1,2) else link(s,x,2,2);
if i<>1 then
if n[i-1,j]='.' then link(x,x-l,0,1);
if i<>s1 then
if n[i+1,j]='.' then link(x,x+l,0,1);
if j<>1 then
if n[i,j-1]='.' then link(x,x-1,0,1);
if j<>l then
if n[i,j+1]='.' then link(x,x+1,0,1)
end
else if (i=1)or(i=s1)or(j=1)or(j=l) then link(x,t,1,2) else link(x,t,2,2);
end;
tot:=0;
for i:=s to t do
if p[i] then
if chu[i]<ru[i] then link1(ps,i,ru[i]-chu[i]) else link1(i,pt,chu[i]-ru[i]);
link1(t,s,maxlongint);
ans:=0;
while bfs(ps,pt) do inc(ans,dfs(ps,maxlongint,pt));
if ans<tot then begin writeln(-1);exit end;
ans:=0;pow[ss]:=0;
while bfs(s,t) do inc(ans,dfs(s,maxlongint,t));
ans:=0;i:=s;
while next[i]<>0 do begin
i:=next[i];
if (flow[i]=1)and(sora[i]<>t) then inc(ans)
end;
i:=t;
while next[i]<>0 do begin
i:=next[i];
if (sora[i]<>s)and(sora[i]<>ps)and(flow[pow[i]]=1) then inc(ans)
end;
writeln(ans>>1)
end;
begin
assign(input,'snake.in');reset(input);
assign(output,'snake.out');rewrite(output);
init;
close(input);close(output)
end.