题目在这里喔
题目描述
农场主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则表示这块土地不适合种草。
输出格式:
一个整数,即牧场分配总方案数除以100,000,000的余数。
思路
这个题因为数据范围很小啊 (1 <= N, M <= 12), 轻易发现应该是一道状压dp
那么再一看到这个
所有整数均为0或1
我们便可以想到使用2进制来进行状压
首先先来考虑一下:每一行的状态应该有(11111111111)2 = (4095)10种
那么使用一个dp[i][j]来记录第i行的每一种情况的总共的方案数
现在状态定义了出来,可是这个状态转移方程如何来写呢?
可以想到:枚举相邻的两行,一直向下推即可(是不是想到了滚动数组手动滑稽 )
于是便有了dp[i][j] = f[i][j] + f[i-1][k];
(其中i,j,k都是需要枚举的,也即是需要三重循环)
所以时间复杂度就为 O(N * 2 ^ (2 * N)) = O(2 ^ N),显然是可以过的
最后
我们只需要统计最后一行的所有情况的值即可。
代码
(因为数据较小,所以小编没有使用滚动数组,有兴趣的读者可以自己试试看)
#include <cstdio>
#include <cctype>
#pragma GCC optimize(2)
template <class T>
void r(T &x)
{
#define gc getchar()
x = 0;
char c = gc;
while (!isdigit(c)) c = gc;
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = gc;
}
const int N = 12;
const int mod = 100000000;
int n, m;
bool field[N + 5][N + 5];
int dp[N + 5][1 << N], farm[N + 5];
bool sta[1 << N];
int main()
{
r(n), r(m);
for (int i = 1;i <= n; i++)
for (int j = 1;j <= m; j++)
r(field[i][j]);
for (int i = 1;i <= n; i++)
for (int j = 1;j <= m; j++)
farm[i] = (farm[i] << 1) + field[i][j];
int tot = 1 << m;
for (int i = 0;i < tot; i++)
sta[i] = (!(i & (i << 1))) && (!(i & (i >> 1)));
dp[0][0] = 1;
for (int i = 1;i <= n; i++)
for (int j = 0;j < tot; j++)
if (sta[j] && ((j & farm[i]) == j))
for (int k = 0;k < tot; k++)
if (!(k & j))
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
long long ans = 0;
for (int i = 0;i < tot; i++)
ans += dp[n][i];
printf ("%lld", ans % mod);
return 0;
}