ZOJ 1100 Mondriaan's Dream【状态压缩】【DP】【DFS】

题目链接

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]);
    }
}

你可能感兴趣的:(ACM)