题目链接:https://vjudge.net/problem/UVA-11270
题意:
用2*1的骨牌填满n*m大小的棋盘,问有多少种放置方式。
题解:
骨牌类的插头DP。
1.由于只需要记录轮廓线上m个位置的放置情况(0或1),且m<=10,2^10 = 1024,故可以用二进制对轮廓线的信息进行压缩。
2.二进制中,第0为代表着当前轮廓线位于第0列的位置的放置情况,以此类推。
3.具体情况分析,设当前格子为a[i][j]:
1) 不放置:前提条件为上面的位置a[i-1][j]已经放置了骨牌。原因:如果上面的位置为空,且这次又不放,那么往后就没有机会填补这个空缺了。
2) 往上放:前提条件为上面的位置a[i-1][j]没有放置骨牌。原因显而易见。
3) 往左放:前提条件为上面的位置a[i-1][j]放置了骨牌,且左边的位置a[i][j-1]没有放置骨牌。原因:情况1)和情况2)的综合。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 11; 18 19 int n, m, cur; 20 LL dp[2][1<<MAXN]; 21 22 int main() 23 { 24 while(scanf("%d%d", &n,&m)!=EOF) 25 { 26 if(n<m) swap(n, m); 27 memset(dp, 0, sizeof(dp)); 28 29 cur = 0; 30 dp[0][(1<<m)-1] = 1; //初始状态,第一个格子的轮廓线上都有插头,所以就防止了往外放 31 for(int i = 0; i<n; i++) 32 for(int j = 0; j<m; j++) 33 { 34 cur ^= 1; 35 memset(dp[cur], 0, sizeof(dp[cur])); 36 for(int s = 0; s<(1<<m); s++) //由于有m个插头,而插头的编号从0开始,故最大状态为(1<<m)-1。 37 { 38 //枚举的是上一个格子的所有状态,即当前格子的轮廓线 39 int up = 1<<j; //位于第j列的插头,即上插头 40 int left = 1<<(j-1); //位于第j-1列的插头, 即左插头 41 bool hav_up = s&up; 42 bool hav_left = s&left; 43 if( hav_up ) //不放置,前提是上插头存在 44 dp[cur][s^up] += dp[cur^1][s]; 45 46 if( i!=0 && !hav_up) //往上边放,前提是上插头不存在 47 dp[cur][s^up] += dp[cur^1][s]; 48 49 if( j!=0 && hav_up && !hav_left ) //往左边放,前提是上插头存在且左插头不存在 50 dp[cur][s^left] += dp[cur^1][s]; 51 } 52 } 53 54 printf("%lld ", dp[cur][(1<<m)-1]); 55 } 56 }