Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 18096 | Accepted: 10357 |
Description
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
Output
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
题意:
给出n * m的棋盘,问用1 * 2的骨牌铺满棋盘的方案数。
分析:
棋盘n,m很小,可以想到状压dp。一般的状压dp是枚举上一维的状态和当前这维状态然后转移。
在蓝书上P384页,也有一种解法。但是网上有另一种做法:http://blog.csdn.net/sf____/article/details/15026397
十分感谢博主的思路。
思路是这样的:
依然定义f[i][j][k],i为第i行,j为第第j列。k为二进制数,1 - k - 1位为当前行状态,k - m 为上一行状态,当前更新把第k位从上一行更新成当前行状态。
二进制中0表示下一行这个位置可以放数(即当前位置不放或者横着放),1表示下一行这个位置不可以放数(即当前位置竖着放)
可以得到dp状态:
dp[i][j][k ^ (1 << j)] += dp[i][j - 1][k]; -- 1 //竖着放 或者不放,因为不可能连续两行不放,所以k ^ (1 << j)和k相同位置必须有一位为1
dp[i][j][k ^ (1 << (j - 1))] += dp[i][j - 1][k]; --2 //从前一格竖着放的转移到当前位置横着放的 条件:当前这位上一格必须放了
因为i 和 j其实是刷表的,可以转移成dp[2][k];就可以了
AC代码:
# include <iostream> # include <cstdio> # include <cstring> using namespace std; const int N = 1 << 11; long long dp[2][N]; int n,m,data; int main(){ while(~scanf("%d %d",&m,&n) && (n + m)){ data = (1 << m); if(m > n)swap(n,m); int now = 0; memset(dp[now],0,sizeof dp[now]); dp[now][0] = 1; for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++){ now ^= 1; memset(dp[now],0,sizeof dp[now]); for(int k = 0;k < data;k++)if(dp[now ^ 1][k]){ dp[now][k ^ (1 << j)] += dp[now ^ 1][k]; if(j && (k & (1 << (j - 1))) && !(k & (1 << j))) dp[now][k ^ (1 << (j - 1))] += dp[now ^ 1][k]; } } } printf("%lld\n",dp[now][0]); } }