//632K 16MS C++
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 13;
const int M = 100000000;
int map[MAX][MAX];
long long solutionNum;
int W;
int H;
#define S (1<<13)
int degree[13] = {0, 1, 2, 4 ,8,
16, 32, 64, 128,
256, 512, 1024, 2048};
int DP[13][S];
int rowNumber[13];
char validStatus[13][S];
void init(int i) {
int j;
int rowMax = 1<<W;
for (j = 0; j < rowMax; j++) {
validStatus[i][j] = 1;
// check adjacent 1 and impossible plant
if ((j & (j<<1)) || (~rowNumber[i]) & j) {
validStatus[i][j] = 0;
}
}
}
void beginDP() {
for (int i = 1; i <= H; i++) {
init(i);
}
int rowMax = 1<<W;
// printf("rowMax %d\n", rowMax);
for (int row = 1; row <= H; row++) {
for (int i = 0; i < rowMax; i++) {
if (row == 1) {
if (i == 0) {
DP[1][0] = 1;
} else if (validStatus[row][i]) {
DP[1][i] = 1;
} else {
DP[1][i] = 0;
}
} else {
if (!validStatus[row][i]) {
DP[row][i] = 0;
} else {
// printf("AAA %d\n", i);
for (int j = 0; j < rowMax; j++) {
if (validStatus[row-1][j] && (i & j) == 0) {
// printf("BBB %d\n", j);
DP[row][i] += DP[row-1][j];
}
}
DP[row][i] %= M;
}
}
}
}
// for (int i = 1; i <= 2; i++) {
// for (int j = 0; j < rowMax; j++) {
// printf("%d ", DP[i][j]);
// }
// printf("\n");
// }
solutionNum = 0;
for (int i = 0; i < rowMax; i++) {
solutionNum += DP[H][i];
}
}
void getSolutionNum() {
beginDP();
printf("%lld\n", solutionNum%M);
}
int main() {
scanf("%d %d", &H, &W);
memset(map, 0, sizeof(map));
memset(DP, 0, sizeof(DP));
memset(rowNumber, 0, sizeof(rowNumber));
for (int y = 1; y <= H; y++) {
int x = 1;
for (; x <= W; x++) {
scanf("%d", &map[x][y]);
// printf("%d %d %d\n", x, y, map[x][y]);
rowNumber[y] += map[x][y]*degree[x];
}
// printf("%d\n", rowNumber[y]);
}
getSolutionNum();
}
妈的,好歹也是经受过2411洗礼的人,这道题我还是给跪了,一开始迫不及待的用了把DFS,不负众望的TLE了.....
后来看了下,才发现跟2411一个套路,都是以行 为递进单位 来进行DP的,也是利用了第n行只能直接影响第n+1行这个特点,
只考虑一行的话,那么就可以进行状态压缩了, 矩形的长最多是12, 一个int(在32位机上应该是32位,更严谨可以用int32_t)将一行的状态(种或者不种)表示出来,
一行的每一个单位格子被这个int数的一位表示(足够,1/0反应 种/不种), 那么又因为最多有12行,一行最多12,DP的数组也最多开到
DP[12][4096]就足够了(其实如果用滚动数组的话,只需要DP[4096],不过我懒得折腾了),对于某个长为L,高为H的矩形,
对于其中一行来说,但从表示其的 int 来说, 一共会有 1<<L 种状态,不过因为题目本身规定了一行并不是所有的格子都可以种庄稼,并且也不能在相邻个各自同时种庄稼,这就意味着这1<<L个状态中,有很多可以直接排除的无效状态:
<1>在本身不能种庄稼的地方种的情况: (~rowNumber[i]) & j, 其中 j 是要检查的状态,rowNumber[i]是该行将将所有能种的地方设为1,不能种的设为0代表的数,(每行的rowNumber在一开始输入的时候就可以求出来)
<2>在相邻格子同时种的情况: j & (j<<1), 一开始没想到这个妙法,还乖乖的一位一位排查。。唉,难道真是智力低下儿童?
上面这个两个状态判断都属于行独立的,及不需要第n-1行的分布情况,就可以判定第n行的这些情况。
所以上面两个可以预处理一下,搞个二维数组保存下。
然后在真正的DP的时候:
<case1> 如果是第一行的话,对于从 0到 1<<L-1的状态 k,只要不是上面两种状态,DP[1][k]就都是1(代表这一种solution)
<case2>如果不是第一行,那么在 0到 1<<L-1的状态k,除了需要不是上面两种情况外, 还要加一个检查:
首先因为第n行的取决与第n-1行的状态,所以这时候需要遍历第n-1行的所有有效状态, 并且对于第n-1行的某个状态j,
还要满足j&k == 0(因为上下相邻的格子也不能同时种庄稼,这样只要k种了, j对应的格子一定要是0), j才是这一行的状态为k时,n-1行可以有的有效状态,
将所有满足k的n-1行的状态的solution数相加就是在第n行,状态为k时,可能的solution数量。
最后,将DP[H][0 <-> 1<<L -1]的所有值累加,就是总的solution数量。
以行为单位处理 和 j&(j<<1)这个trick 再mark一下.