【问题描述】
cza 特别喜欢吃海苔,怎么吃也吃不够。 cza 的生日到来时,他的父母给他买了许许多多的
海苔和一个生日蛋糕。海苔是一个 1*2或2*1的长方形,而蛋糕则是一个n*m 的矩阵。蛋
糕上有一些蜡烛占据了位置,其他地方都可以放海苔。 cza的父母让cza把海苔尽可能多的
放在蛋糕上,但是海苔不能够重叠放置。 cza想把海苔留着自己以后慢慢吃,可又不敢违背
父母,于是他决定放一少部分在蛋糕上。为了不使父母起疑, cza必须确保放置完海苔后,
蛋糕上不存在 1*2 或 2*1的空白以放置更多的海苔。cza 想知道这样得花多少海苔,请帮助
他求出满足这样放置所需的最少海苔数。
(祝 cza 用餐愉快)
【输入格式】
输入的第一行是蛋糕的规模 n和m(注意是n行m列)
接下来的 n 行每一行含 m个字符。每个字符要么是".",表示空白;要么是"*",表示蜡烛
【输出格式】
输出文件只包含一个整数 k,表示满足题目要求的最小海苔数。
【输入样例】
3 3
...
.*.
...
【输出样例】
3
【数据范围】
对于 30%的数据 N<=5, M<=5
对于100%的数据N<=70, M<=7
分析:
一看数据范围就知道是要状态压缩的一道题,因为列数m的范围是1到7,就用一个二进制数来表示一行的状态。由于这一题不用把整个都填满,所以上一行的状态也会影响到当前行的选择,应当在记录状态时表示两行。
设f[i,opt1,opt2]是当前为第i行,上一行状态为opt1,当前行状态为opt2时的方案总数,用一个dfs来进行状态的转移就搞定了。转移时需要考虑的情况略多,需要细心。
代码:
var
i,j,p:longint;
f:array[0..71,0..130,0..130]of longint;
a:Array[1..71]of longint;
inf,maxopt,n,m:longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end;
procedure dfs(k,opt1,opt2,opt3,step:longint);
begin
if (k>0)and(opt1 and (1 shl (k-1))=0) and (opt2 and (1 shl (k-1))=0)then
exit;
if (k>1)and(opt2 and (1 shl (k-1))=0) and (opt2 and (1 shl (k-2))=0)then
exit;
if k=m then begin
f[i,opt2,opt3]:=min(f[i,opt2,opt3],f[i-1,j,p]+step);
exit;
end;
dfs(k+1,opt1,opt2,opt3,step);
if (opt3 and (1 shl k)=0) and (opt2 and (1 shl k)=0)then
dfs(k+1,opt1,opt2 or (1 shl k),opt3 or (1 shl k),step+1);
if (k<m-1) and (opt2 and (1 shl(k+1))=0) and(opt2 and (1 shl k)=0)then
dfs(k+2,opt1,opt2 or (1 shl k) or (1 shl (k+1)),opt3,step+1);
end;
procedure init;
var
ans:longint;
s:string;
begin
readln(n,m);
for i:=1 to n do begin
readln(s);
a[i]:=0;
for j:=1 to m do
if s[j]='*'then a[i]:=a[i] or (1 shl (j-1));
end;
a[n+1]:=0;
maxopt:=1 shl m - 1;
fillchar(f,sizeof(f),10);
inf:=f[0,0,0];
f[0,maxopt,a[1]]:=0;
for i:=1 to n do
for j:=0 to maxopt do
for p:=0 to maxopt do
if f[i-1,j,p]<>inf then
dfs(0,j,p,a[i+1],0);
ans:=2147483647;
for i:=0 to maxopt do ans:=min(ans,f[n,i,0]);
writeln(ans);
end;
begin
assign(input,'cake.in');
assign(output,'cake.out');
reset(input);
rewrite(output);
init;
close(input);
close(output);
end.