【状压DP】POJ 2411 Mondriaan's Dream

 POJ 2411 Mondriaan's Dream

题意:给一个n*m大的区域,问用1*2的矩形块填充,能够刚好填满的填充方式有多少?

FEELING:真的吹爆神犇!!www,这个方法也太巧妙了叭qaq!!!!

大神思路蒟蒻理解:

dp[i][s]: 第 i 行状态为s的种类数(保证前i - 1行是全部覆盖的)

我们用01表示(i , j)格子有没有被覆盖,那么每一行都会有一个01串表示的状态。

很明显,第 i 行的状态只受第 i - 1 行状态的影响。如果(i - 1, j)为0,也就是没有被覆盖,那么(i, j)必须要有一个竖着放的矩形块放在(i - 1, j)和(i, j)处,以保证前i - 1行都能被全部覆盖。所以如果枚举到的合法的第 i - 1 行的状态是s,那么第 i 行的状态一定是 ~s,我们设为now_s。(这里的合法状态是指:能够保证它前一行之前的区域都已经被全部覆盖的状态)

很明显第 i 行矩形块必须要竖着放的方式的种类数就是第 i - 1 行状态为s时的种类数。

这就完了嘛?显然没有!第 i 行的矩形块目前只放完了竖着的,那剩余空位置不是还可以横着放吗?无论怎么横着放,不放,放一个,或放几个(只要能空间足够),都是合法状态。因为我们竖着放的矩形块已经保证了第 i 行的状态合法。我们需要把这些合法状态的种类数都更新。由此有了深搜!!!

跑了一个dfs维护每一个合法状态的种类数是多少。

最开始先跑第0行的横着放的合法状态,因为第0行是边界,不需要竖着放保证上一行之前的被完全覆盖,所以这一行的合法状态就是横着放的所有状态。然后从第i = 1行开始枚举i - 1行的每一个合法状态推第 i 行的种类数.

最后答案是dp[n - 1][(1 << m) - 1]

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <limits>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e4 + 5;
int n, m;
ll dp[15][1 << 15];
ll line_num;//竖着放矩形块有多少种方式
void dfs(int row, int sta, int pos)//每一行的某个合法状态下还能继续横着放矩形块的方式的种类数
{
    if(pos >= m)//pos是第row行的第几个数位。跑完所有的数位当然就加上竖着放的种类数
    {
        dp[row][sta] += line_num;
        return;
    }
    dfs(row, sta, pos + 1);//更新第row行的初始状态【还没有横着放矩形块】种类数
    if(pos <= m - 2 && !(sta & (1 << pos)) && !(sta & (1 << pos + 1)))
        dfs(row, sta | (1 << pos) | (1 << pos + 1), pos + 2);//更新横着放至少一个矩形块的合法状态的种类数
}
int main()
{
    while(~scanf("%d%d", &n, &m) && (n || m))
    {
        memset(dp, 0, sizeof(dp));
        line_num = 1;//第一行的当然初始化为1
        dfs(0, 0, 0);
        for(int row = 1; row < n; row ++ )
        {
            for(int s = 0; s < (1 << m); s ++ )
            {
                if(dp[row - 1][s])//如果状态不合法就没必要继续往下跑
                {
                    int now_s = ~s & ((1 << m) - 1);
                    line_num = dp[row - 1][s];//第row行竖着放的种类数有line_num种
                    dfs(row, now_s, 0);
                }
            }
        }
        printf("%lld\n", dp[n - 1][(1 << m) - 1]);
    }
    return 0;
}
发布了180 篇原创文章 · 获赞 54 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/103910579