题目
题目传送门
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入输出格式
输入格式:
第一行:两个整数M和N,用空格隔开。
第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。
输出格式:
一个整数,即牧场分配总方案数除以100000000的余数。
输入输出样例
输入样例
2 3
1 1 1
0 1 0
输出样例#1:
9
题解
-
正解:状压 D P DP DP
-
设 h [ i ] h[i] h[i]表示第 i i i行的状态 如:" 110 101 111 110 \quad101\quad111 110101111"
-
设 g [ i ] g[i] g[i]表示该状态是否合法( 0 ≤ i < 2 m 0\leq i < 2^m 0≤i<2m)
-
设 f [ i ] [ j ] f[i][j] f[i][j]表示第 i i i行状态为 j j j所能得到的最大方案数
-
h [ i ] h[i] h[i]的求法
h[i] = (h[i] << 1) + a[i][j];
- g [ i ] g[i] g[i]的求法(判断左边和右边那位数字与中间的是否相同)
g[i] = (!(i & (i << 1)) && !(i & (i >> 1)));
code
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9;
template <typename T>
inline void read(T &s) {
s = 0;
T w = 1, ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
s *= w;
}
int n, m;
int h[15], g[1 << 15];
int a[15][15], f[15][1 << 15];
int main() {
read(n), read(m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
read(a[i][j]);
h[i] = (h[i] << 1) + a[i][j];
}
}
for (int i = 0; i < (1 << m); ++i)
g[i] = (!(i & (i << 1)) && !(i & (i >> 1)));
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < (1 << m); ++j) {
if (g[j] && (j & h[i]) == j) {
for (int k = 0; k < (1 << m); ++k) {
if (!(k & j)) {
f[i][j] += f[i - 1][k];
f[i][j] %= mod;
}
}
}
}
}
int ans = 0;
for (int i = 0; i < (1 << m); ++i) {
ans += f[n][i];
ans %= mod;
}
printf("%d\n", ans);
return 0;
}