pku 2411 modriaans dream解题报告
很显然利用了状态压缩dp,我们假设:
00ß-------à00 横放
1ß----------à1 竖放
那么以sample:2 4为例,得到的答案为一下:
1、
0000
0000:
2、
0000 1100
1100 或者: 0000
3、
1111 0000
0000 或者: 1111
4、
1001 0000
0000 或者: 1001
5、
0011 0000
0000 或者:0011
所以,我们对于状态转移方程需进行三种操作:
1、 往每个格子+1;
2、 往每2个格子+2;
3、 取反操作.
然后我们假设第一行为0行,全为0,一层一层dp下去,这样我们就实现了状态压缩dp.
AC代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
__int64 dp[12][2048];
int n, m;
/*
就这两种状态转移:
横着放 00 -> 00
竖着放 0 -> 1
*/
//1表示已经放了,不能放了,0表示还能放.
void dfs(int i, int Initial, int Target, int s)
{
//Initial是初始状态,jj是目标状态
if (s == m)
{
dp[i + 1][Target] += dp[i][Initial];
// printf("i(%d) j(%d) (i+1)%d (jj)%d/n", i, Initial, i+1, Target);
}
else if ((Target & (1 << s)) == 0)
{
dfs(i, Initial, Target | (1 << s), s + 1);
if (s < m - 1 && (Target & (1 << (s + 1))) == 0)
{
dfs(i, Initial, Target, s + 2);
}
}
else
{
dfs(i, Initial, Target & ~(1 << s), s + 1); //~表示按位取反运算符
}
}
/*
经典的状态压缩DP.f[i][j]表示第i行,方格排布为二进制数j(第k位上为1表示凸出一个格子,为0表示不凸出)的方案数.
用DFS进行状态转移.
*/
int main()
{
freopen("1.txt", "r", stdin);
int i, j;
while (scanf("%d%d", &n, &m), n + m != 0)
{
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (i = 0; i < n; i++)
{
for (j = 0; j < (1 << m); j++) //共有1<<m种情况可以考虑
{
if (dp[i][j])
{
dfs(i, j, j, 0);
}
}
}
printf("%I64d/n", dp[n][0]);
}
return 0;
}