E31 状态压缩DP 蒙德里安的梦想

视频链接:https://www.bilibili.com/video/BV1cv411b7EG/

HDU1400 Mondriaan's Dream

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=12, M=1<<N; 
bool st[M];         //st[i]存储合并列的状态i是否合法  
long long f[N][M];  //f[i][j]表示摆放第i列,状态为j时的方案数 

int main(){
  int n, m;
  while(cin>>n>>m, n||m){
    // 预处理:判断合并列的状态i是否合法 
    // 合并列即两列状态合并之意,对应后面的st[j|k]      
    // 如果合并列的某行是1表示横放,是0表示竖放 
    // 如果合并列不存在连续的奇数个0,即为合法状态     
    for(int i=0; i< 1<<n; i++){
      st[i]=true;
      int cnt=0;                //记录合并列中连续0的个数 
      for(int j=0; j<n; j++){
        if(i>>j & 1){           //如果是1
          if(cnt & 1){          //如果连续0的个数是奇数 
            st[i]=false; break; //记录i不合法           
          }
        }
        else cnt++;             //如果是0,记录0的个数 
      }
      if(cnt & 1) st[i]=false;  //处理高位0的个数 
    }
    
    memset(f, 0, sizeof f); 
    f[0][0]=1;
    for(int i=1; i<=m; i++)     //阶段:枚举列 
    for(int j=0; j < 1<<n; j++) //状态:枚举第i列的状态 
    for(int k=0; k < 1<<n; k++) //状态:枚举第i-1列的状态   
      if((j&k)==0 && st[j|k])   //两列状态兼容:不出现重叠的1,不出现连续的奇数个0 
        f[i][j] += f[i-1][k];
    
    printf("%lld\n",f[m][0]); //第m列不横放即答案 
  }
  return 0;
}

 

posted @ 2023-04-10 10:17  董晓  阅读(472)  评论(0编辑  收藏  举报