题目链接
http://www.icpc.moe/onlinejudge/showProblem.do?problemId=100
思路
题意很简单,给你个h*w的格子,让你用2*1的长方形去填满它,问有几种填法。
题意简单不代表好写啊,这TM怎么搞。。。比赛时想破脑子也只能想个暴力出来,妥妥TLE。
后来搜了下题解终于做出来了,是状压DP+dfs。这是我第二次碰到搜索和状压组合的题目了,但状压+DP+搜索这么复杂的还是第一次。
设dp[state][i]
表示把第i行摆成sate状态的方法种数,state是个w位的二进制数,1表示这一格已经被某个长方形遮住了,0表示空的。
然后对每行仅考虑横着放和竖着向上放,竖着向下放不考虑,因为完全可以用下一行的竖着向上方等价代替。
每拿到一行,先对其取反,得出下一行的初始状态。因为上一行为0的位置下一行一定要用竖块给填上。
例如:1011001
取反:0100110,0表示还能放的位置。
然后dfs枚举这一行所有可能的状态,然后dp[state][i]+=pre
pre表示母状态的种数。
一开始pre=1,把第一行枚举下。
最后答案就是dp[111111(w个1)][h]
。
还有个小坑就是取反的时候记得把前导零清零,不然就乱套了。
AC代码
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <list>
using namespace std;
typedef long long ll;
ll dp[1<<11][12];
int h,w;
ll pre;
void dfs(int r,size_t s,int cur)
{
if(cur>=w-1)
{
dp[s][r]+=pre;
return;
}
dfs(r,s,cur+1);
if((s&(1<<cur))==0 && cur<w-1 && (s&1<<(cur+1))==0 )
dfs(r,(s|(1<<cur)|(1<<cur+1)),cur+2);
}
int main()
{
while(scanf("%d%d",&h,&w),h)
{
memset(dp,0,sizeof dp);
if((h&1) && (w&1))
{
printf("0\n");
continue;
}
pre=1;
dfs(0,0,0);
for(int r=0 ; r<h-1 ; ++r)
{
for(size_t s=0 ; s<(1<<w) ; ++s)
{
pre=dp[s][r];
if(pre==0) continue;
dfs(r+1,~s&((1<<w)-1),0);
}
}
printf("%lld\n",dp[(1<<w)-1][h-1]);
}
}