从简单的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;
}