zoukankan      html  css  js  c++  java
  • 铺砖头问题(完美)——爆搜&&插头DP

    题意

    给定一个 $n imes m$ 的格子,每个格子被染成了黑色或白色。现在要用 $1 imes 2$ 的砖块覆盖这些格子,要求块与块之间互不重叠,且覆盖了所有白色的格子,但不覆盖任意黑色格子。求一共有多少种覆盖方法。结果对 $M$ 取余。($1 leq nleq 15, 1 leq mleq 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($nmcdot 2^{nm})$,会超时。也无法使用记忆化搜索。

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

    不确定的只有每一列里还没有查询的格子种的最上面的一个,共 $m$ 个。从而可以把这 $m$ 个格子通过状态压缩编码进行记忆化搜索,复杂度为 $O(nmcdot 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
    ", crt[0]);
    }
    
    int main()
    {
        //init color
        scanf("%d%d", &n, &m);
        solve();
    }

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

  • 相关阅读:
    《程序员修炼之道+从小工到专家》读后感2
    java在子类中,调用父类中被覆盖的方法
    长按文本全复制
    NSTimer 定时器总结
    对URL编码
    Mysql find_in_set 效率问题
    php 运算符优先级
    使用layui上传控件问题
    xcode11发版一直卡在App Store验证过不去
    iOS13禁用深色模式
  • 原文地址:https://www.cnblogs.com/lfri/p/11521375.html
Copyright © 2011-2022 走看看