铺砖头问题(完美)——爆搜&&插头DP

题意

给定一个 $n \times m$ 的格子,每个格子被染成了黑色或白色。现在要用 $1 \times 2$ 的砖块覆盖这些格子,要求块与块之间互不重叠,且覆盖了所有白色的格子,但不覆盖任意黑色格子。求一共有多少种覆盖方法。结果对 $M$ 取余。($1 \leq n\leq 15, 1 \leq m\leq 15$)

分析

爆搜,从最左上方开始放置,从左至右,从上到下,每找到一种方案返回1.

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 1000000007;

int n, m;
const int maxn = 15+5;
bool color[maxn][maxn];     //false为白,true为黑
bool used[maxn][maxn];

int rec(int i, int j)
{
    if(j == m)  return rec(i+1, 0);     //进入下一行
    if(i == n)  return 1;    //已经覆盖所有空格
    if(used[i][j] || color[i][j])  return rec(i, j+1);     //不需要在(i, j) 上放置砖块

    //尝试2种放法
    int res = 0;
    used[i][j] = true;

    //横着放
    if(j+1 < m && !used[i][j+1] &&!color[i][j+1])
    {
        used[i][j+1] = true;
        res += rec(i, j);
         used[i][j+1] = false;
    }
    //竖着放
    if(i+1 < n && !used[i+1][j] && !color[i+1][j])
    {
        used[i+1][j] = true;
        res += rec(i, j+1);
        used[i+1][j] = false;
    }

    used[i][j] = false;
    return res % mod;
}

int main()
{
       //init color    
        scanf("%d%d", &n, &m);
        printf("%d,", rec(0, 0));
    }
}

这个方法的时间复杂度为 $O($nm\cdot 2^{nm})$,会超时。也无法使用记忆化搜索。

但是仔细思考会发现,实际参数并没有这个多种可能。

不确定的只有每一列里还没有查询的格子种的最上面的一个,共 $m$ 个。从而可以把这 $m$ 个格子通过状态压缩编码进行记忆化搜索,复杂度为 $O(nm\cdot 2^{m})$.

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 1000000007;

int n, m;
const int maxn = 15+5;
const int M = 1000000007;
bool color[maxn][maxn];     //false为白,true为黑

int dp[2][1 << maxn];

void solve()
{
    int* crt = dp[0], *nxt = dp[1];
    crt[0] = 1;
    for(int i = n-1;i >= 0;i--)
        for(int j = m-1;j >= 0;j--)
        {
           for(int used = 0; used < (1 << m); used++)
           {
               if((used >> j & 1) || color[i][j])
                    nxt[used] = crt[used & ~(1 << j)];   //不需要在(i, j)放置砖块
                else
               {
                   //尝试2种放法
                   int res = 0;
                   if(j+1 < m && !((used >> (j+1)) & 1) && !color[i][j+1])  //横着放
                        res += crt[used | (1 << (j+1))];
                   if(i+1 < n && !color[i+1][j])
                        res += crt[used | (1 << j)];         //竖着放
                   nxt[used] = res % M;
               }
           }
            swap(crt, nxt);
        }
    printf("%d\n", crt[0]);
}

int main()
{
    //init color
    scanf("%d%d", &n, &m);
    solve();
}

From:《挑战程序设计竞赛》

猜你喜欢

转载自www.cnblogs.com/lfri/p/11521375.html