Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 16640 | Accepted: 9606 |
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
用 2 * 1 的骨牌铺满 w * h 的棋盘,有多少中铺法。
状态压缩的轮廓dp。
从第一个格开始,从左往右,从上往下对每个格进行dp。
转移的状态是已处理和未处理的交界线上的格子的状态。把前一个格子的决策导致的当前状态,转移到当前格子决策产生的下一个状态。
具体的说,如果前一个格子铺上后,当前格子是黑色的,则这个格子不能放骨牌,只能转移到同列的下一个格子是白色的状态。如果是白色的,则可以选择竖着放或者横着放。横着放就转移到 j + 1 个格子是黑色的状态(状态转移是横向移动的,如果恰好是行尾,则不能横放),竖着放则转移到同列下一个格子是黑色的状态。以此进行转移就好了。初始状态,也就是对第一个格子决策时面对的状态是,前一个格子(不存在)只转移了一种状态过来,就是所有格子都是白色,种数为1,dp[0][0] = 1。其他状态种数都是0。
最终状态,决策完最后一个格子后,转移到下一个轮廓线上所有个子都为白色,说明恰好平铺。这样的种数就是待求答案。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn = 12;
int w, h;
LL dp[2][1<<maxn];
// cur 处理完前一个格后当前轮廓的状态
// next 处理完当前格后下一个轮廓的状态
LL *cur = dp[0], *next = dp[1];
int main()
{
while(1)
{
scanf("%d %d", &h, &w);
if(w == 0 && h == 0) break;
if(w < h) swap(w, h);
memset(dp, 0, sizeof dp);
// -1格填满后,第0行只有状态0这一种情况,且种数为1
cur[0] = 1;
for(int i= 0; i< h; i++)
for(int j= 0; j< w; j++)
{
// 初始化
memset(next, 0, sizeof(dp[0]));
for(int s= 0; s< 1<<w; s++)
{
// 当前状态下j格是黑的,它只能转移到j格是白色的情况
if(s>>j & 1) next[s & ~(1<<j)] += cur[s];
// j格是白的
// 骨牌竖着摆,转移到j格为黑
else next[s | 1<<j] += cur[s];
// 骨牌横着摆,转移到j+1格为黑
if(j < w-1 && !(s>>j & 3)) next[s | 1<<(j+1)] += cur[s];
}
swap(cur, next);
if(i == h-1 && j == w-1)
printf("%lld\n", cur[0]);
}
}
return 0;
}