一、状态压缩dp(4)CZA的蛋糕

4CZA的蛋糕(cake
【问题描述】
cza 特别喜欢吃海苔,怎么吃也吃不够。 cza 的生日到来时,他的父母给他买了许许多多的
海苔和一个生日蛋糕。海苔是一个
1*22*1的长方形,而蛋糕则是一个n*m 的矩阵。蛋
糕上有一些蜡烛占据了位置,其他地方都可以放海苔。
cza的父母让cza把海苔尽可能多的
放在蛋糕上,但是海苔不能够重叠放置。
cza想把海苔留着自己以后慢慢吃,可又不敢违背
父母,于是他决定放一少部分在蛋糕上。为了不使父母起疑,
cza必须确保放置完海苔后,
蛋糕上不存在
1*2 2*1的空白以放置更多的海苔。cza 想知道这样得花多少海苔,请帮助
他求出满足这样放置所需的最少海苔数。
(祝
cza 用餐愉快)
【输入格式】
输入的第一行是蛋糕的规模
nm(注意是nm列)
接下来的
n 行每一行含 m个字符。每个字符要么是".",表示空白;要么是"*",表示蜡烛
【输出格式】
输出文件只包含一个整数 k,表示满足题目要求的最小海苔数。
【输入样例】
3 3
...
.*.
...
【输出样例】
3
【数据范围】
对于 30%的数据 N<=5M<=5

对于100%的数据N<=70M<=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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值