上下界 又见上下界 noi模拟题snake

 
题目描述 
一些蛇覆盖了一个网格。每个格子要么是一个障碍物,要么是蛇的一部分。每条蛇
占据了一条折线(拐角处只能水平和竖直连接),且至少占据两个格子。蛇与蛇之间不
重叠,蛇也不会与自己重叠。每条蛇还必须满足以下两个条件中的一个: 
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.  



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值