状态压缩DP --> POJ 3254 Corn Fields

【题目来源】
http://poj.org/problem?id=3254

农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米。非常遗憾,部分土地是不育的,无法种植。而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。(注意:这里是上下左右边缘,不是两斜对角边缘)。现在给定土地的大小,请你求出共有多少种种植方法。土地上什么都不种也算一种方法。

【输入格式】
第 1 行包含两个整数 M 和 N。
第 2…M+1 行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0 表示该块土地不育。

【输出格式】
输出总种植方法对 100000000 取模后的值。

【算法分析】
以样例为例,我们可以一行一行的考虑。
假如对于每一行,用1表示放牛,0表示不放牛。
第一行的状态为:000、001、010、011(舍弃)、100、101、110(舍弃)、111(舍弃)
符合题意的状态只有5个,所以第一行有5种方案。
第二行的状态为:000、010
但是第二行中的010和第一行中的010冲突,所以如果第二行状态为010时,共有4种方案。状态为000时,有5种方案,所以一共有4+5=9种方案。
分析完我们会发现,对于每一行,都可以一个01串来表示这一行的状态,而这个状态可以用一个十进制整数来代替,也就是说,把这个状态压缩成了一个十进制整数,所以称为是状态压缩。棋盘模型是状态压缩最好的展示舞台之一。
本题中,dp[i][j] 就表示第i行状态为j时的方案数
状态压缩dp中最常见的就是位运算,因为位运算可以很方便的判断当前状态是否合法。

以下情况不合法:
①土地本身不能耕种。(j&a[i])!=j
②同一行内有两棵玉米挨在一起。j&(j<<1)!=0
③相邻两行有玉米挨在一起。k&j!=0

-------------------------------------------------------------

无后效性:当前状态只受直接前驱状态影响
状态:用二进制表示状态。整数和状态是一一对应的,因为每个整数转化成二进制都是唯一的。
状态转移:是状态就可以转移。可以用整数的加减结果表示状态之间的转移。
状态压缩:其实就是将一个二进制转化成一个整数的意思。由于整数可以作为数组的下标,所以这样做会方便编码。

【程序代码】

#include <bits/stdc++.h>
using namespace std;

int a[15];
int dp[15][1<<13];

const int mod=1e8;
int m,n;

int main() {
	while(cin>>m>>n) {
		memset(a,0,sizeof(a));
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		int total=1<<n;
		for(int i=1; i<=m; i++) {
			for(int j=0; j<n; j++) {
				int x;
				cin>>x;
				a[i]|=(x<<j);
			}
		}
		for(int i=1; i<=m; i++) {
			for(int j=0; j<total; j++) { //枚举所有状态
				if((j&a[i])!=j) continue;//剔除掉与题目给的条件不符的
				if(j&(j<<1)) continue;//剔除列相邻的
				for(int k=0; k<total; k++) {
					if(k&j) continue;//剔除行相邻的
					dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
				}
			}
		}
		
		int ans=0;
		for(int i=0; i<total; i++) {
			ans=(ans+dp[m][i])%mod;
		}
		cout<<ans<<endl;
	}
	
	return 0;
}

/*
2 3
1 1 1
0 1 0

9
*/


【参考文献】
https://blog.csdn.net/qq_42936517/article/details/86547980?utm_medium=distribute.pc_relevant.none-task-blog-baidulandingword-3&spm=1001.2101.3001.4242

https://www.cnblogs.com/sweepy/p/13432840.html

https://blog.csdn.net/arliastark/article/details/80541787

https://blog.csdn.net/foreverlxm/article/details/22910461?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-5.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-5.channel_param

https://blog.csdn.net/y990041769/article/details/24658419?utm_source=blogkpcl3

https://blog.csdn.net/Timeclimber/article/details/79422157?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param






 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值