zoukankan      html  css  js  c++  java
  • # Acwing 291蒙德里安的梦想

    Acwing 291蒙德里安的梦想

    题意

    (N*M)的棋盘分割成若干个(1*2)的长方形的方案总数。

    思路

    一共有N行,考虑以某一行为分界,将整个棋盘分成两半,第(i)行的状态通过第(i-1)行转移过来。

    由于是分割,(横放的长方形只占一行,不会影响下一行的状态,用于填充该行的空)。所以只需要考虑竖着放的长方形有多少种方法,剩下的空都使用横放的长方形填充(只有一种方案),竖放的长方形的方案数就是答案。

    (f[i][j])表示第(i)行每一列是否有(1*2)长方形的起点,该行每一列的状态通过二进制压缩在j中,就是说(j)的二进制的每一位的10表示对应列选还是不选((j=(5)_{10}=(101)_2)表示第一三列有长方形的起点,第二列则没有,该列是否有长方形终点暂时不用管,之后处理会用if判断)。

    对于每一行放置完竖放的长方形后,留下来的空,如果有连续的奇数个空连在一起,就无法填充横放的长方形,那么这个竖放长方形的方案就不合理。需要预处理每一行的每一种竖放长方形的方案是否合理。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=12;
    int n,m;
    bool isok[1<<N];
    long long f[N][1<<N];
    
    int main(){
        while (cin>>n>>m,n||m){
    
            for(int i=0;i<1<<m;i++){//枚举每一列是否有使用,有使用代表该列有出现长方形的起点或者终点
                isok[i]=true;
                int cnt=0;
                for(int j=0;j<m;j++){
                    if(i>>j&1){
                        if(cnt&1){isok[i]=false;break;}//连续的奇数个空格,标记该状态不可用
                        cnt=0;
                    }
                    else cnt++;
                }
                if(cnt&1)isok[i]=false;
            }
    
            memset(f,0,sizeof f);
            f[0][0]=1;
            for(int i=1;i<=n;i++){//枚举每一行
                for(int j=0;j<1<<m;j++)//枚举该行每一列的状态
                    for(int k=0;k<1<<m;k++){//枚举该行上一个状态,也就是上一行的列的使用状态
                        if(j&k || !isok[j|k])continue;//j,k枚举的是列是否出现起点,如果k出现起点,那么下一行k的位置就会出现终点,如果该列又出现起点就会冲突,(j|k)代表了第i行被长方形占据的位置(包括起点和终点)
                        f[i][j]+=f[i-1][k];
                    }
            }
            cout<<f[n][0]<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    养生与健康
    vue + elementui 使用多选按钮实现单选功能
    生活小方
    和业务相关的工具函数
    vue + element ui开发过程中需要注意的几个点
    【转】webpack中关于source map的配置
    【转】移动前端手机输入法自带emoji表情字符处理
    webpack相关
    vue项目优化
    运维集群架构演变之美 【转】
  • 原文地址:https://www.cnblogs.com/sstealer/p/13298906.html
Copyright © 2011-2022 走看看