Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 15272 | Accepted: 8806 |
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
Source
这题也是状压dp,状态压缩方式与之前做的那题大致相同,判断略微复杂。
显然,对于第i行第j列,如果i-1行j列没有被填,那么必定要竖填一个(不然无法填满);否则,则可以横填或不填。
但是由上一行考虑本行有点麻烦,所以lkb比较喜欢由这一行推到下一行的方式。
在找出可行的状态的时候可以不用嵌套循环的枚举而改用dfs,会好些。
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 11 + 5;
const int maxm = 11 + 5;
int n, m;
//保存在当前状态下,下一行的可能状态
int cnt = 0;
int state[1 << maxm];
//状压dp数组
long long f[maxn][1 << maxm];
void dfs(int k, int p) {
//深搜找出所有可能的状态
//当前状态为k(二进制)
//p为二进制"指针"
if(p + 1 == m) {
state[cnt++] = k;
return;
}
dfs(k, p + 1); //不放
if((k & (1 << p)) || (k & (1 << (p + 1)))) return; //有障碍无法横着放
dfs(k | (3 << p), p + 1); //横着放
}
int main() {
cin >> n >> m;
do {
if(((n * m) & 1) == 1) cout << 0 << endl; //总格子数为奇数,不可能摆满
else {
memset(f, 0, sizeof f);
cnt = 0;
dfs(0, 0); //初始化第一行状态
for(int i = 0; i < cnt; i++) f[1][state[i]] = 1;
int upperLim = 1 << m; //把2^m保存起来避免重复计算
for(int i = 1; i < n; i++)
for(int j = 0; j < upperLim; j++)
if(f[i][j]) { //如果第i行状态j不可行则不考虑
cnt = 0;
dfs(upperLim - j - 1, 0); //找出状态j下一行可能的所有状态
for(int k = 0; k < cnt; k++)
f[i + 1][state[k]] += f[i][j];
}
cout << f[n][upperLim - 1] << endl; //填满的可能方案总数
}
cin >> n >> m;
} while(n != 0 && m != 0);
return 0;
}