Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 14596 | Accepted: 8431 |
Description
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
Output
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
问题描述:
给出一个n*m的棋盘,及一个小的矩形1*2,问用这个小的矩形将这个大的棋盘覆盖有多少种方法。
由于我们放置小矩形的时候可以横着放也可以竖着放,那么就会产生不同的方法,但是必须满足不产生空的未覆盖的空格子。
首先我们用二进制1 0表示在某一个问题放置或者不放置,那么有m列的棋盘,每一行有2*m个状态了,我们现在就找出每行之间的规律。
对于一个矩形有3种方法:横放,竖放,不放。由于第i行只跟第i-1行的放置有关系,因为我们必须保证第i-1行使放满的,现在用dp[i][state]表示第i行
状态为state的方法,那么dp[i][curstate]=sum{dp[i-1][prestate]}.
1 横放
如果第i行第d列我们选择横放,那么第i行的第d列及d+1列都是1了,第i-1行第d列及d+1列也都必须为1(保证是满的),及状态转移为:
d=d+2,curstate=curstate<<2|3,prestate=prestate<<2|3.
2竖放
第i行第d列我们选择竖放,那么第i行第d列为1,第i-1行d列必须是0,(因为我们是竖着放的,如果前一行不是空的如何能放下呢),状态转移:
d=d+1,curstate=curstate<<1|1,prestate=prestate<<1.
3不妨
第i行第d列不妨,那么第i-1行d列肯定是1,(保证是满的),状态转移:
d=d+1,curstate=curstate<<1,prestate=prestate<<1|1.
这个题目采用记忆化搜索,对已经计算出的状态值方法记录,还有就是初始化的时候将dp[0][2<<m-1]=1,这样第0行使放满的,就不用单独进行初始化了(单独初始化的时候,由于是第一行,不存在竖着放的可能)。
我们的目标就是求dp[n][2<<m-1]了, 源码如下:
#include <stdio.h>
#include <string.h>
int row, col;
long long dp[12][1<<11];
void dfs(int r, int c, int pre, int now)
{
if(c == col)
{
dp[r][now] += dp[r-1][pre];
return;
}
if(c + 1 <= col)
{
dfs(r, c + 1, pre<<1, (now<<1)|1);
dfs(r, c + 1, (pre<<1)|1, now<<1);
}
if(c + 2 <= col)
{
dfs(r, c + 2, (pre<<2)|3, (now<<2)|3);
}
}
int main()
{
while(scanf("%d%d", &row, &col) != EOF)
{
if(!row)
break;
if((row * col) % 2 == 1)
{
printf("0\n");
continue;
}
if(row < col)//行列变化,让列是小的值,因为行是线性的,而列的可能值是指数的
{
row ^= col;
col ^= row;
row ^= col;
}
memset(dp, 0, sizeof(dp));
dp[0][(1<<col) - 1] = 1;
for(int i = 1; i <= row; i++)
dfs(i, 0, 0, 0);
printf("%lld\n", dp[row][(1<<col) - 1]);
}
return 0;
}