农场主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: 复制
2 3
1 1 1
0 1 0
输出样例#1: 复制
9
先说下我数组都是表示什么,dp【i】【j】表示第i行是j状态时,前i行的可行个数。F【i】表示题目给的每i层的状态。ok【】表示没有相邻的土地的状态。
主要有一点:搞懂F【i】&ok【j】 = ok【j】 这个东西,题目给的每行可能会有相邻的肥沃土地,而ok【】里存的都是没有相邻的土地。两个取&之后还等于ok【j】表示第i行可以取ok【j】这种状态,比如下边几个例子。
F 110010100
ok 101010101 // F&ok = ok 不能,
可以看到ok里有的1在F中并没有,所以不会相等。
F 111111111
ok 101010101 这种就是 F&ok == ok
可以看到ok中的1都是原有的1。
然后就是看上下没有相邻的,也就是上一行1和这一行的1错开,也就是这两行&后==0;
当然前提是两个的状态都得可行,都得是ok【某一个】&F【i】 = ok【某一个】 并且 ok【某一个】 & F【i-1】 = ok【某一个】。
代码:
#include <bits/stdc++.h>
#define mod 100000000
#define ll long long
using namespace std;
int n, m, F[15], x;
int dp[15][1 << 15];
int ok[555];
int main()
{
memset(dp, 0, sizeof(dp));
memset(ok, 0, sizeof(ok));
memset(F, 0, sizeof(F));
cin >> m >> n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
cin >> x;
F[i] = (F[i] << 1) + x;
}
x = 0;
int full = (1 << n) - 1;
for (int i = 0; i <= full; i++)
if((i & (i<<1)) == 0) ok[++x] = i; //这个就是优化的地方,这样下边只需要遍历x次,而不是full次。
dp[0][0] = 1;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= x; j++)
{
if((ok[j] & F[i]) == ok[j]) //如果这一行ok【j】这种状态可行
{
for(int k = 1; k <= x; k++)
{ //找上一层那种状态可行并且和这一行可行状态错开。
if((ok[k] & F[i-1]) == ok[k] && !(ok[k] & ok[j]))
{
dp[i][ok[j]] = (dp[i][ok[j]] + dp[i-1][ok[k]]) % mod;
//都满足,就是这一层这种状态的个数加上上层可行状态的个数。
}
}
}
}
}
ll sum = 0;
for (int i = 1; i <= x; i++)
sum+=dp[m][ok[i]]; //这个就是把最后一行每种可行状态相加就行,
//如果不可行就是0,所以就不用再判断了。dp【i】【j】表示第i行是j状态时,前i行的可行个数
sum%=mod;
cout << sum << endl;
return 0;
}