状态DP与二进制及位运算密切相关。
在这道题中,对于 一行0或1 所表示的状态,我们就可以把它们当作一个数的二进制,而它们所代表的“数”,即是表示这一行的状态。
比如(111) 就可以用 一个数 7 来表示 这一行的状态
那么接下来就要去判断 我们安排的装态 是否合法,由题意,只能在“1”上放牧,而且任意两只牛相邻是不合法的,
那么要考虑的主要有三个方面:
1: 一行安排的状态,要与这一行实际状态相容,即只能在“1”上放牧;
设该行实际状态为 i ,我们安排的状态为 j,
则合法状态满足 j & ( ~i ) == 0
理解:
将 i 取反再与 j 相与,结果为零,
如果 j 在某一位上为0,显然是合法的;如果为1,则 ~i 对应位置上必为0才能使最终结果为零;
而此时i 实际对应位== 1; 可以放牧,与 j 不冲突。。。(位运算好晕。。。)
2: 安排的状态本身左右相斥,即不能同时为1;
对于安排的状态j, 满足 j&(j<<1)==0 即可,比较简单,自己想想
3.安排的状态上下相斥;(而且当前行状态仅与上一行相关,而与其他行无关,即无后效性)
对于上一行的状态 k, 本行的状态 j,(假设它们已与实际状态相容)
满足 j & k == 0 即可;(自己推)
状态数目 的最大值为 1<<2^(M+1); 但实际上 满足条件2 的的状体就很少了,(cnt==614);
可以将它们用一个state[]数组先记录下来,循环时遍历这些合法状态即可。。。
当然,每一行的实际状态也要用map_r_state[]数组记录下来 (数组名字自己取。。。。)
dp[i][j] 表示第i行状态为state[j]时 的方案数 最终结果将dp[M][j], j={0,1,,,cnt-1};遍历相加即可
贴代码:
#include<iostream>
#include<cstring>
using namespace std;
const int MOD = 100000000;
const int MAXN = (1<<13)+5;
int state[MAXN]; //实际合法状态会很少
//当前状态是否合法(左右相容)
//记录合法状态
int cnt = 0;
void state_is_legal()
{
for(int j=0; j<MAXN; ++j)
if( !(j&(j<<1)) ) //j&(j<<1))==0
state[cnt++] = j; //只要记录可行状态就好了
}
//是否符合实际情况
bool r_and_v_is_legal(int n, int j)
{
if( !(j&(~n)) ) return true;
else return false;
}
//上下两行状态是否相容
bool l_and_n_is_legal(int j, int k)
{
if( !(j&k) ) return true;
return false;
}
int main()
{
memset(state,0,sizeof(state));
state_is_legal();
//cout<<"cnt "<<cnt<<endl;
int M,N;
while(cin>>M>>N)
{
int map[M+1][N+1];
int map_r_state[M+1];
memset(map_r_state,0,sizeof(map_r_state));
for(int i=1; i<=M; ++i)
for(int j=1; j<=N; ++j)
{
cin>>map[i][j];
if(map[i][j]) map_r_state[i] += (1<<(N-j)); //注意下标,这里是从1开始
}
int dp[M+1][MAXN];
memset(dp,0,sizeof(dp));
//初始化第一行
for(int j=0; j<cnt; ++j)
if(r_and_v_is_legal(map_r_state[1],state[j]))
dp[1][j] = 1;
for(int i=2; i<=M; ++i)
for(int j=0; j<cnt; ++j)
{
if(r_and_v_is_legal(map_r_state[i],state[j]))
{
for(int k=0; k<cnt; ++k)
if(r_and_v_is_legal(map_r_state[i-1],state[k]))
{
if(l_and_n_is_legal(state[j],state[k]))
dp[i][j] += dp[i-1][k]; //直接用j表示第j个合法状态
}
}
}
int ans = 0;
for(int j=0; j<cnt; ++j)
ans = (ans+dp[M][j])%MOD;
cout<<ans<<endl;
}
return 0;
}