POJ 3254(状态压缩DP)

【题目大意】一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)
【解析】根据题意,把每一行的状态用二进制的数表示,0代表不在这块放牛,1表示在这一块放牛。首先很容易看到,每一行的状态要符合牧场的硬件条件,即牛必须放在能放牧的方格上。这样就能排除一些状态。另外,牛与牛之间不能相邻,这样就要求每一行中不能存在两个相邻的1,这样也能排除很多状态。然后就是根据上一行的状态转移到当前行的状态的问题了。必须符合不能有两个1在同一列(两只牛也不能竖着相邻)的条件。这样也能去掉一些状态。然后,上一行的所有符合条件的状态的总的方案数就是当前行该状态的方案数。
【状态表示】dp[state][i]:在状态为state时,到第i行符合条件的可以放牛的方案数
【状态转移方程】dp[state][i] =Sigma dp[state'][i-1] (state'为符合条件的所有状态)
【DP边界条件】首行放牛的方案数dp[state][1] =1(state符合条件) OR 0 (state不符合条件)不可迷信权威,要善于发现问题的根源,在模仿的前提下自己解决很重要;

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
#define mod 100000000
int A[20][20];
int dp[20][1000],M,N,top = 0;
int cpu[20];//代表i行的不能走的情况
int state[1000];//代表不考虑0时所有可以放的情况
void Init()
{
     top = 0;
     int total = (1 << N) - 1;
     for(int i = 0;i <= total;i ++)
     {
          if(!( i & (i << 1) ) )
               {
                    state[++ top] = i;//求出每一行不考虑0时所有可以放的情况

               }
     }
}
int main()
{
     int i,j,k;
     cin >> M >> N;
     for(i = 1;i <= M;i ++)
          for(j = 1;j <= N;j ++)
               cin >> A[i][j];
     for(i = 1;i <= M;i ++)
          for(j = 1;j <= N;j ++)
           if(A[i][j] == 0)
              cpu[i] += (1 << (j-1));//统计每一行中0的情况
     Init();

     for(i = 1;i <= top;i ++)
     {
          if(!(state[i] & cpu[1]))
                    dp[1][i] = 1;
     }//对第一行的情况进行初始化
     for(i = 2;i <= M;i ++)
          for(j = 1;j <= top;j ++)
     {
          if(!(state[j] & cpu[i]))//先判断这个状态是不是可以在本行放
          {
               for(k = 0;k <= top;k ++)
                    if(!(state[j] & state[k]) &&
                       !(state[k] & cpu[i-1]))
                       dp[i][j] = (dp[i][j] + dp[i-1][k]) % mod;//如果不合上一行的可放状态有冲突的话,当前状态
                                                                //就可以有上一行的状态转移过来
          }
     }
     int ans = 0;
     for(i = 1;i <= top;i ++)
          ans = (ans + dp[M][i]) % mod;
     printf("%d\n",ans);
     return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值