题目
题目描述
农场主 J o h n John John新买了一块长方形的新牧场,这块牧场被划分成 M M M行 N N N列 ( 1 ≤ M ≤ 12 ; 1 ≤ N ≤ 12 ) (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) (1≤M≤12;1≤N≤12),每一格都是一块正方形的土地。 J o h n John John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 J o h n John John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
J o h n John John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入
第一行:两个整数 M M M和 N N N,用空格隔开。
第 2 2 2到第 M + 1 M+1 M+1行:每行包含 N N N个用空格隔开的整数,描述了每块土地的状态。第 i + 1 i+1 i+1行描述了第 i i i行的土地,所有整数均为 0 0 0或 1 1 1,是 1 1 1的话,表示这块土地足够肥沃, 0 0 0则表示这块土地不适合种草。
输出
一个整数,即牧场分配总方案数除以 100000000 100000000 100000000的余数。
样例输入
2 3
1 1 1
0 1 0
样例输出
9
题解
简单的状压,我们定义二维的
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示到第
i
i
i行符合条件的第
j
j
j个状态的种草总方案
转移很好想
d
p
[
i
]
[
j
]
=
∑
d
p
[
i
−
1
]
[
k
]
dp[i][j] = \sum dp[i - 1][k]
dp[i][j]=∑dp[i−1][k]
我们只需要先预处理出每一行符合条件的状态( 1 1 1表示种草, 0 0 0表示不种),然后三重循环转移,最后枚举一下求和就可以了
具体实现见代码
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const LL mod = 100000000;
LL ans;
LL dp[15][40000];
int m, n;
int sum[15], a[15][15], zt[15][40000];
bool check(int x, int y) {
int w = n;
while(y) {
if(a[x][w] == 0 && y % 2 == 1)
return 0;
y >>= 1;
w --;
}
return 1;
}
void prepare() {
int p = (1 << n) - 1;
for(int i = 0; i <= p; i ++)
if(!((i << 1) & i)) {
for(int j = 1; j <= m; j ++) {
if(check(j, i))
zt[j][++ sum[j]] = i;
}
}
}
int main() {
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; i ++)
for(int j = 1; j <= n; j ++) {
int x;
scanf("%d", &x);
if(x == 1) a[i][j] = 1;
else a[i][j] = 0;
}
prepare();
for(int i = 1; i <= sum[1]; i ++)
dp[1][i] = 1;
for(int i = 2; i <= m; i ++)
for(int j = 1; j <= sum[i]; j ++)
for(int l = 1; l <= sum[i - 1]; l ++)
if(!(zt[i][j] & zt[i - 1][l]))
dp[i][j] = (dp[i][j] + dp[i - 1][l]) % mod;
for(int j = 1; j <= sum[m]; j ++)
ans = (ans + dp[m][j]) % mod;
printf("%lld\n", ans);
return 0;
}