/**//* 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; }
|