Corn fields poj 3254 (状压dp入门)

5 篇文章 0 订阅

状态压缩动态规划:
   动态规划的状态有时候比较恶心,不容易表示出来,需要用一些编码技术,把状态压缩的用简单的方式表示出来。
典型方式:当需要表示一个集合有哪些元素时,往往利用2进制用一个整数表示。


再普及一下位运算:

& 按位与      如果两个相应的二进制位都为1,则该位的               结果值为1,否则为0
| 按位或      两个相应的二进制位中只要有一个为1,该位              的结果值为1
^ 按位异或    若参加运算的两个二进制位值相同则为0,               否则为1
~ 取反        ~是一元运算符,用来对一个二进制数按位取        反,即将0变1,将1变0
<< 左移       用来将一个数的各二进制位全部左移N位,        右补0
>> 右移       将一个数的各二进制位右移N位,移到右端        的低位被舍弃,对于无符号数,高位补0

好吧,回到这题

这道题也就是俗称的牛吃草问题,【题目大意】一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛(不包括斜着的),即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)
【Input】1<=n<=12,1<=m<=12
【Output】一个mod100000000的整数
一看到如此小的数据范围,又是求方案数,自然而然就联想到了状态压缩的DP。

话不多说,上代码:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int dp[20][maxn];
int st[maxn];
int maps[maxn];
int judge1(int x){//这里原理是判断当前状态是否存在两个1相邻,根据&的性质,只有两个都为1才返回1
	return x&(x<<1);
}
int judge2(int x,int y){//判断是否可以将这个状态放到地图上
	return (maps[x] & st[y]);
}
int main(){
	int i,j,k,m,n;
	int x,y,z;
	while(scanf("%d%d",&m,&n)!=EOF){
		memset(maps,0,sizeof(maps));
		memset(dp,0,sizeof(dp));
		memset(st,0,sizeof(st));
		for(i=1;i<=m;i++){
			for(j=1;j<=n;j++){
				scanf("%d",&x);
				if(!x) maps[i] += (1<<j-1);
			}
		}
		int cnt = 0;
		for(i=0;i<(1<<n);i++){
			if(!judge1(i)){//不停地去除没用的状态
				st[++cnt] = i;
			}
		}
		for(i=1;i<=cnt;i++){
			if(!judge2(1,i)){
				dp[1][i] = 1;
			}
		}
		for(i=2;i<=m;i++){
			for(j=1;j<=cnt;j++){
				if(judge2(i,j))continue;//枚举这一层可能存在的状态
				for(k=1;k<=cnt;k++){
					if(!judge2(i-1,k)){//枚举上一层存在的状态
						if(!(st[k]&st[j]))判断上下相邻是否可放
						dp[i][j]+=dp[i-1][k];//状态转移
					}
				}
			}
		}
		int ans=0;
		for(i=1;i<=cnt;i++){
			ans=(ans+dp[m][i]%100000000)%100000000;
		}
		printf("%d\n",ans%100000000);
	}
	return 0;
} 


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值