题意:John买了一块矩形的地。他想在上面种草喂奶牛,但是有的格子比较贫瘠不能种草,我们用0表示。否则用1表示。由于奶牛不喜欢挤在一起,所以任何两个相邻格子不能都种上草。现在问你有多少种种草的方式。当然,不种草也算一种方式。
题解:第 i 行的第 j 中状态由前一行决定。故枚举第 i - 1 行中所有与 j 相容的状态。
#include <iostream>
using namespace std;
#define Num 100000000
#define N (1<<13)
bool state[13][N]; /* state[i][j] 表示第i行可以化成状态j,每一行的其他状态都是由其初始状态转移的到得 */
int dp[13][N], row[13]; /* row[i]表示第i行的初始状态 */
int Max, R, C;
void calState ( int i )
{
for ( int k = 0; k <= Max; k++ )
{
if ( ((~row[i]) & k) || (k & (k<<1)) ) /* 一开始用的是(row[i]&(~k)),没注意到位运算会修改掉原来的值,WR了很久 */
state[i][k] = 0;
else state[i][k] = 1;
}
}
void operDp ()
{
int i, j, k;
for ( i = 0; i <= Max; i++ ) /* 预先把第一行的值求出来 */
{
if ( state[1][i] )
dp[1][i] = 1;
}
for ( i = 2; i <= R; i++ )
{
for ( j = 0; j <= Max; j++ )
{
if ( ! state[i][j] ) /* 若第i行的状态j不存在,直接用第i行下一个状态 */
continue;
for ( k = 0; k <= Max; k++ )
if ( state[i-1][k] && !(j & k) ) /* 若第i-1行的状态k存在,且状态k与状态j直接不冲突,则dp[i][j]累加 */
dp[i][j] += dp[i-1][k];
dp[i][j] %= Num;
}
}
}
int main()
{
scanf("%d%d",&R,&C);
Max = 1 << C;
memset(state,0,sizeof(state));
memset(dp,0,sizeof(dp));
int i, j, flag;
for ( i = 1; i <= R; i++ )
{
for ( j = 0; j < C; j++ )
{
scanf("%d",&flag);
row[i] |= ( flag << j );
}
calState ( i );
}
operDp();
int ans = 0;
for ( i = 0; i <= Max; i++ )
ans += dp[R][i];
printf("%d\n",ans % Num);
return 0;
}