• POJ 2411: Mondriaan's Dream


    POJ 2411: Mondriaan's Dream

    题目链接:http://poj.org/problem?id=2411

    题目大意:在$h imes w$($1 leqslant h,w leqslant 11$)的网格中放满$1 imes 2$的木条,问有几种放置方案。

    状压dp

    定义横向放置的木条为$11$(横向),纵向放置的木条为$_1^0$(纵向).

    这样检查转移的合法性只需要检查前一行状态$s_{i-1}$与当前行状态$s_i$的位或($s_{i-1}|s_i$)是否全为$1$,且横向的$1$是否均匹配.

    定义状态$dp[i][s]$表示第$i$行状态为$s$的方案数,若状态$s_{i-1}$由状态$s_i$转移合法,则有$dp[i][s_{i-1}]+=dp[i-1][s_i]$.

    最后一行不能放置向下的纵向木条,即最后一行全为$1$,故$ans=dp[n-1][(1<<m)-1]$.

    可以预处理合法的状态并标记以降低复杂度。

    总时间复杂度为$O(2^{MAX{m}}+n2^{2m})$.

    代码如下:

     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 typedef long long ll;
     5 int n,m;
     6 ll dp[15][1<<11];
     7 bool s[1<<11];
     8 bool valid(int s){
     9     int tot=0;
    10     while(s){
    11         if(s&1)tot++;
    12         else if(tot&1)return 0;
    13         s>>=1;
    14     }
    15     return tot%2==0;
    16 }
    17 void init(){
    18     for(int i=0;i<(1<<11);++i)
    19         s[i]=valid(i);
    20 }
    21 bool check(int x,int y){
    22     if((x|y)+1!=(1<<m))return 0;
    23     return s[x&y];
    24 }
    25 int main(void){
    26     init();
    27     while(~scanf("%d%d",&n,&m)){
    28         if(n==0&&m==0)break;
    29         memset(dp,0,sizeof(dp));
    30         for(int i=0;i<(1<<m);++i)
    31             if(s[i])dp[0][i]=1;
    32         for(int i=1;i<n;++i){
    33             for(int j=0;j<(1<<m);++j)if(dp[i-1][j]){
    34                 for(int k=0;k<(1<<m);++k)if(check(j,k)){
    35                     dp[i][k]+=dp[i-1][j];
    36                 }
    37             }
    38         }
    39         printf("%lld
    ",dp[n-1][(1<<m)-1]);
    40     }
    41 }
  • 相关阅读:
    5js
    4js
    3js
    2js
    1js
    7css
    6css
    5css
    Django高级项目实战-开发企业级问答网站,学Django就这了
    Java日志第27天 2020.8.1
  • 原文地址:https://www.cnblogs.com/barrier/p/6769657.html
Copyright © 2020-2023  润新知