题目
Description
cza特别喜欢吃海苔,怎么吃也吃不够。cza的生日到来时,他的父母给他买了许许多多的海苔和一个生日蛋糕。海苔是一个1*2或2*1的长方形,而蛋糕则是一个n*m的矩阵。蛋糕上有一些蜡烛占据了位置,其他地方都可以放海苔。cza的父母让cza把海苔尽可能多的放在蛋糕上,但是海苔不能够重叠放置。cza想把海苔留着自己以后慢慢吃,可又不敢违背父母,于是他决定放一少部分在蛋糕上。为了不使父母起疑,cza必须确保放置完海苔后,蛋糕上不存在1*2或2*1的空白以放置更多的海苔。cza想知道这样得花多少海苔,请帮助他求出满足这样放置所需的最少海苔数。
Input
输入的第一行是蛋糕的规模n和m(注意是n行m列)
接下来的n行每一行含m个字符。每个字符要么是”.”,表示空白;要么是”*”,表示蜡烛。
Output
输出文件只包含一个整数k,表示满足题目要求的最小海苔数。
Sample Input
3 3
…
.*.
…
Sample Output
3
Data Constraint
对于30%的数据N<=5,M<=5
对于100%的数据N<=70, M<=7
思路
这道题比较有难度。
用状压DP,只需要考虑一个格子中有或没有海苔/蜡烛。
DP,设F[i,s1,s2]表示第i行状态为s1,第i+1行状态为s2。
则每次枚举两行状态,根据第i-1行状态判断,通过dfs填充第i行空缺,顺便填一部分第i+1行。
分三种情况讨论。
① 当前位置不放。
条件就是上一格和左一个不能有空缺(如果空缺就会有1*2的空隙,并且不能在下一行补回)。
或者当前这一格有东西(放不下)。
②水平放置。
有地方放就可以放。
③垂直放置。
同上。
代码
var
a:array[1..71] of longint;
f:array[0..71,0..127,0..127] of longint;
g:array[0..127] of boolean;
n,m,i,j,k,l,ans,ll:longint;
ch:char;
procedure init(t,s,l:longint);
begin
if t>m then
begin
g[s]:=true;
exit;
end;
if l=1 then
init(t+1,s*2,0);
init(t+1,s*2+1,1);
end;
function min(x,y:longint):longint;
begin
if x<y then min:=x
else
min:=y;
end;
procedure dfs(t,x,y,z,s:longint);
begin
if t>ll then
begin
f[i,y,z]:=min(f[i,y,z],f[i-1,j,k]+s);
exit;
end;
if ((x and t>0) and ((t=1) or ((y and (t div 2))>0))) or (y and t>0) then//①不放
dfs(t*2,x,y,z,s);
if (y and t=0) and (z and t=0) then//②垂直
dfs(t*2,x,y+t,z+t,s+1);
if (t*2<ll) and (y and t=0) and (y and (t*2)=0) then//③水平
dfs(t*2,x,y+t*3,z,s+1);
end;
begin
assign(Input,'cake.in'); reset(Input);
assign(Output,'cake.out'); rewrite(Output);
readln(n,m);
ll:=(1 shl m)-1;
init(1,0,1);
for i:=1 to n do
begin
for j:=1 to m do
begin
read(ch);
a[i]:=a[i]*2;
if ch='*' then
inc(a[i]);
end;
readln;
end;
fillchar(f,sizeof(f),$7F);
f[0,ll,a[1]]:=0;
for i:=1 to n do
begin
for j:=0 to ll do
if g[j]=true then//判断上一行是否可行
begin
for k:=0 to ll do
if f[i-1,j,k]<2000000000 then
dfs(1,j,k,a[i+1],0);
end;
end;
ans:=maxlongint;
for i:=0 to ll do
ans:=min(ans,f[n,i,0]);
writeln(ans);
close(Input); close(Output);
end.