POJ3254:状态DP入门级理解

来源:互联网 发布:乾坤量能指标源码 编辑:程序博客网 时间:2024/06/01 22:36

状态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;}
0 0