【算法】动态规划——状态压缩dp入门 (poj3254 Corn Fields)

【题目链接】http://poj.org/problem?id=3254

【题意】给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。

【状态表示】dp[state][i]:在状态为state时,到第i行符合条件的可以放牛的方案数

【状态转移方程】dp[state][i] =Sigma dp[state’][i-1] (state’为符合条件的所有状态)

【DP边界条件】首行放牛的方案数dp[state][1] =1(state符合条件) OR 0 (state不符合条件)

【代码】来源:https://blog.youkuaiyun.com/harrypoirot/article/details/23163485

#include <cstdio>
#include <cstring>
using namespace std;
 
#define mod 100000000
int M,N,top = 0;
//top表示单行最多的状态数
 
int state[600],num[110];  
//state存放每行所有的可行状态(即没有相邻的状态
//
 
int dp[20][600];
//dp[i][j]:对于前i行数据,每行在状态j时的解
int cur[20];
//cur[i]表示的是第i行整行的情况
 
inline bool ok(int x){	//判断状态x是否可行
   if(x&x<<1)	return false;//若存在相邻两个格子都为1,则该状态不可行
   return true;
}
void init(){			//遍历所有可能的状态
   top = 0;
   int total = 1 << N; //遍历状态的上界
   for(int i = 0; i < total; ++i){
       if(ok(i))state[++top] = i;	
   }
}
inline bool fit(int x,int k){ //判断状态x 与第k行的实际状态的逆是否有‘重合’
   if(x&cur[k])return false; //若有重合,(即x不符合要求)
   return true;  //若没有,则可行
}
 
int main(){
    while(scanf("%d%d",&M,&N)!= EOF){
       init();
       memset(dp,0,sizeof(dp));
       for(int i = 1; i <= M; ++i){
           cur[i] = 0;
           int num;
           for(int j = 1; j <= N; ++j){  //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
                scanf("%d",&num);  //表示第i行第j列的情况(0或1)
               if(num == 0) //若该格为0	
				   cur[i] +=(1<<(N-j)); //则将该位置为1(注意要以相反方式存储,即1表示不可放牧
           }
       }
       for(int i = 1;i <= top;i++){
           if(fit(state[i],1)){  //判断所有可能状态与第一行的实际状态的逆是否有重合
                dp[1][i] = 1;  //若第1行的状态与第i种可行状态吻合,则dp[1][i]记为1
           }
       
	   }
 
	   /*
	   状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
		*/
       for(int i = 2; i <= M; ++i){  //i索引第2行到第M行
           for(int k = 1; k <= top; ++k){
                if(!fit(state[k],i))continue; //找出一组与第i行相符的state[k]
                for(int j = 1; j <= top ;++j){
                   if(!fit(state[j],i-1))continue;  //找出一组与第i-1行相符的state[j]
                   if(state[k]&state[j])continue;  //判断state[k]是否与第state[j]行冲突
                   dp[i][k] = (dp[i][k] +dp[i-1][j])%mod;  //若以上皆可通过,则将'j'累加到‘k'上
                }
           }
       }
       int ans = 0;
       for(int i = 1; i <= top; ++i){ //累加最后一行所有可能状态的值,即得最终结果
           ans = (ans + dp[M][i])%mod;
       }
       printf("%d\n",ans);
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值