一维和二维铺瓷砖

从简单的xujcoj入手

xujcoj1062

看起来就像是递归,实际上就是的题目

这种题目我们先画几个观察一下


定义dp[n]为3*n的有dp[n]种放法

由肉眼观察法我们知道

3*1的有1种

3*2的有2种

3*3的有7种

dp方程就是dp[n]=dp[n-1]+2*dp[n-2]+7*dp[n-3]了吗,当然不是,因为有一个包含问题

比如第2种可以由两次dp[n-1]得到

接下来我们看看包含的问题

第2种可以通过2个3*1的得到,也就是说,3*2的中包含了1种3*1的,所以dp的时候只能是1*dp[n-2]而不是2*dp[n-2]

第4种由3个3*1,可以由3次dp[n-1]获得,第5种由1个3*2和1个3*1,可以由dp[n-1]+dp[n-2]获得.第6种1个3*1和1个3*2,可以由dp[n-2]+dp[n-1]获得,一共3种是包含的,所以是,4*dp[n-3]而不是7*dp[n-3]

所以dp[n]=dp[n-1]+dp[n-2]+4*dp[n-3]

#include<iostream>
int main()
{
	const int maxN = 101;
	int dp[maxN] = { 0,1,2,7 }, t, n;
	for (int i = 4; i < maxN; i++)
		dp[i] = (dp[i - 1] + dp[i - 2] + 4 * dp[i - 3]) % 100000007;
	scanf("%d", &t);
	while (t--){
		scanf("%d", &n);
		printf("%d\n", dp[n]);
	}
	return 0;
}

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

poj2411也是编程之美4.2章节的课后题目

题目大意:用2*1和1*2的瓷砖填满h*w的地板

解法:

我们用0表示这个格子不放瓷砖,1表示这个格子放瓷砖

1.显然,(i-1,j)是0的时候,(i,j)必然是1,因为要想这个格子不放,就必须下一行竖着放回去

2.当到了最后一行,必然全是1,因为我们是要铺满,所以不能是0,毕竟没有下一行了

3.每一行都是0,1组成的,所以自然能联想到二进制,也就是说,每一行我们都可以用一个二进制来表示,那么每一行的状态就是从0到pow(2,w)-1,我们知道二进制左移一位就是乘二,右移一位就是除以二,那么pow(2,w)-1=(1<<w)-1

那么我们用dp[i][j]来表示第i行,状态为j的铺法有dp[i][j]种

4.每一行的状态只和上一行的有关系,比如0011,取反之后1100,那么这一行的状态有1100,1111这几种,都和1100有关.1的地方已经固定死了,0的地方考虑不放,和横着放,如果不放,dfs下一列.如果第j列横着放,首先要有两个连续的0,不用dfs第j+1列,直接考虑dfs第j+2列

dfs到了w的时候,会有一个状态,可能和之前的1100不一样了,假设i-1行的状态是pre,现在dfs完的状态是now,

dp[i][now]+=dp[i-1][pre]


注意事项:按位取反符号位也是会取反的,0的补码假设是000000,~0=111111,反码就是111110,原码就是100001,也就是-1

#include<iostream>
#include<cstring>
const int maxN = 12;
int n, m;
long long dp[maxN][1 << maxN], method = 1;
//dfs row表示第几行,state表示当前的状态,pos表示当前到了第几列
void dfs(const int &row, int state, int pos)
{
	if (pos == m) {
		dp[row][state] += method;
		return;
	}
	//考虑不放
	dfs(row, state, pos + 1);
	//pos<=m-2因为要留两个0,1<<pos表示取到第pos位的,同理pos+1位的,取到完了&state
	//0&1=0表示这一位可以放1&1=1表示这一位不能放
	if (pos <= m - 2 && !(state & (1 << pos)) && !(state & (1 << (pos + 1))))
		dfs(row, state | (1 << pos) | (1 << (pos + 1)), pos + 2);
}
int main()
{
	while (scanf("%d%d", &n, &m), n | m) {
		memset(dp, 0, sizeof(dp));
		method = 1;
		dfs(1, 0, 0);//初始化
		for (int i = 2; i <= n; i++)
			for (int j = 0; j<(1 << m); j++)//用二进制表示状态,0011状态的是12,高位放后面好算
				if (dp[i - 1][j]) {//如果i-1行状态为j的铺法数量为0,那么就没有考虑的必要了
					method = dp[i - 1][j];
					dfs(i, (~j)&((1 << m) - 1), 0);//细节取反(1<<m)-1的符号位是0,所以~j之后,符号位就是0&1=0
				}
		printf("%lld\n", dp[n][(1 << m) - 1]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39942341/article/details/79406042