题意
给你一个n*m的矩阵(农田),1表示可以种植,0表示不能种植。且上下和左右每个单元不能相邻。问有多少种种植方法(可以不种植)。
样例
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
1 2 3
0 4 0
种植一棵树(1,2,3,4)4种,两棵(13,14,34)3种,三棵(134)1种,零棵树1种,sum = 4+3+1+1 = 9。
题解
第一行可行状态有
1 0 0,0 1 0,0 0 1,1 0 1,0 0 0 。 5种
第二行可行状态是
0 0 0(1状态),0 1 0(2状态)
1状态下可行下第一行可行状态有全5种,2状态可行下有(1 0 0, 0 0 1,1 0 1,0 0 0)4种。总共9种。
所以dp的转移方程式
dp[i][state] +=dp[i-1][pre_state] = dp[i-1][pre_state(k1)]+dp[i-1][pre_state(k2)]+......+dp[i-1][pre_state(km)]
即此行状态可行时,上一行满足的状态总和就是此状态的可行情况。那么此行的可行状态就是此行所有可行状态总和。
所以ans = dp[n][state(j1)]+dp[n][state(j2)]+...+dp[n][state(jm)]。
代码
#include<cstdio>
#include<algorithm>
#include<string.h>
#include <string.h>
using namespace std;
typedef long long ll;
int dp[15][1<<15];
bool is[15][15]; //记录哪一处可种植
int n, m;
const int Mod = 1e8;
bool check(int x, int state)
{
if(state & (state<<1)) return false;
for(int i = 1; i <= m; i++)
{
if(!is[x][i]) //若这处是不能种植的
{
if( ((1<<(m-i)) & state) != 0) //但是这种状态下此处种了树
return false;
}
}
return true;
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF)
{
//memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d", &is[i][j]);
dp[0][0] = 1;
ll ans = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < (1<<m); j++)
{
if(check(i,j)) {
for(int k = 0; k < (1<<m); k++)
if(!(k&j)) dp[i][j] += dp[i-1][k];
}
if(i == n) ans = (ans+dp[i][j]) % Mod;
}
}
printf("%I64d", ans);
}
return 0;
}