 /**//*
40*10的地图,有一些'*'必须用方块覆盖到
而每块方块只能覆盖长度为1*2
问最少需要的方块数

状态压缩dp,主要就是枚举当前行的状态to,上一行的状态from,由from的值去更新to的值

骨牌覆盖类求方案数的,一般都需要枚举所有情况,即放、不放
但这种求最优值的,我一开始枚举所有情况,导致超时!!
需要人肉优化
比如,这里空的位置肯定不用放东西,直接下一列

这道题,转移时只需考虑当前row和上一行row-1,
对于列col 分4种情况,
00 01 10 11
1表示该位置是'*'
分这4种情况:
①不放 状态构造为 to<<1 , from<<1
②上一行有'*' 状态构造为 to<<1 , from<<1|1
③当前行有'*' 由于上一行没有,就没必要竖放了,直接横放了,竖放不会导致最优解
然后还要考察col+1是否有'*',有的话状态要加上去
当然,该位置可以不放,为了给下一行覆盖
④上下行都有'*'
不放的话,上一行就需要有覆盖了
横放,上一行肯定就要被覆盖了
竖放,上一行可选择被覆盖,或者没被覆盖
但选择被覆盖是不会导致最优解的,因为横放的时候就会考虑上一行该位置要被覆盖了

求最优值(不是求方案),没必要转移时 from -> to 把一些明显不优的状态from转移到to去
如对于'O'位置,可以放,但肯定不去放它,放了不会更优的,所以不进入这个分支了
分支数决定了规模,人肉优化,不进入没必要的分支
但求方案数目的话,就要考虑所有情况了,放、不放之类的
*/
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<cmath>
#include<string>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<set>
#include<list>
#include<numeric>
#include<cassert>

using namespace std;

const int INF = 1000000000;

char field[50][20];
int dp[50][1<<10];
int H, W;

 void dfs(int row, int col , int s1 , int s2 , int num) {//当前检查col
 /**//*if(col > W){
return;
}*/
 if(col >= W) {//因为可能在最后一列水平覆盖,所以出格了
 if(col>W) {//注意>>
s1 >>= 1;
s2 >>= 1;
}
dp[row][s1] = min(dp[row][s1] , dp[row-1][s2] + num);
return;
}
int code = (field[row][col] == '*')*2 + (field[row-1][col] == '*');
 switch(code) {
case 0:
dfs(row, col+1, s1<<1, s2<<1, num);
break;
case 1:
dfs(row, col+1, s1<<1, s2<<1|1, num);//用|去覆盖(row-1,col)跟(row-1,col)自己覆盖效果一样
break;
case 2:
dfs(row, col+2, (s1<<2)|2|(field[row][col+1] == '*'), (s2<<2)|(field[row-1][col+1] == '*'), num+1);
//nothing
dfs(row, col+1, s1<<1, s2<<1, num);
break;
case 3:
//nothing
dfs(row, col+1, s1<<1, s2<<1|1, num);
dfs(row, col+1, s1<<1|1, s2<<1, num+1);
//dfs(row, col+1, s1<<1|1, s2<<1|1, num+1); s2<<1|1在下面的状态会考虑到的,而且下面的不会比它差 所以不用再dfs
dfs(row, col+2, (s1<<2)|2|(field[row][col+1] == '*'), (s2<<2)|2|(field[row-1][col+1] == '*'), num+1);
break;
}
}

int main()
  {
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
#endif

int T;
 for(scanf("%d",&T); T-- ;) {
scanf("%d%d",&H,&W);
 for(int i = 1 ; i <= H; i++) {
scanf("%s",field[i]);
}
 for(int row = 1 ; row <= H ; row++) {
fill(dp[row] , dp[row] + (1<<W), INF);
dfs(row,0,0,0,0);
}
int ans = INF;
int mask = 0;
 for(int i = 0; i < W ; i++) {
mask <<= 1;
mask |= (field[H][i] == '*');
}
 for(; mask < (1<<W);mask++) {
ans = min(ans, dp[H][mask]);
}
printf("%d\n",ans);
}
return 0;
}
|