poj2411(状态压缩dp)
Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 14198 | Accepted: 8191 |
Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt
of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical
tilings multiple times.
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
详细说明请看:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html
/* 最上面的为第1行,最下面为第n行 从上到下按行DP 其中一行的状态我们用一个二进制表示,0表示没有被覆盖,1表示被覆盖了 最后得到一个01串,这个串变回十进制就是一个状态 定义状态dp[i][s],表示前i-1行已经放满,第i行的状态为s的方案数 状态转移方程为 dp[i][s]=sum{ dp[i-1][ss] } ,其中状态s与状态ss兼容 这个状态转移方程的内涵在于理解s和ss何为兼容 首先我们约定一个放置方法,就是竖着放的时候,我们暂且将其称为“上凸型摆放” 因为竖放必然占据第i-1行和第i行,我们约定这个方块是属于第i行的,也就是说它凸上去了 那么要在第i行的第j列竖放一个方块的话,第i-1行第j列必须没有方块 也就是说,第i行的放置是受到第i-1行的限制的,反过来说在第i行竖放了方块,也会影响第i-1行的状态 所以这样就可以讲解一下状态转移方程了,前i-2行已经放满了,第i-1行的状态为ss(dp[i-1][ss]) 此时在第i行开始放一些方块,放的方法不定,可能横放可能竖放,但是按这个方案放完后 第i-1行刚好被填满,且第i行的状态变为了s,所以不难想到第i-1行的状态ss到第i行的状态s这个转移是唯一的 所以有 dp[i][s]=sum{ dp[i-1][ss] } 最后我们详细讨论一下s和ss在什么情况下是兼容的 1.第i行的第j列为1,第i-1行的第j列为1,这样的话,说明第i行的第j列一定不是竖放而是横放否则会与第i-1行的第j列冲突 所以马上紧接着判断第i行第j+1列,如果是1,那么满足横放的规则,同时也要第i-1行第j+1列也要为1,否则的话这个格子没办法填充, 成立后向左移动两格 不满足上述条件的,就是两个不兼容或者不合法的状态 2.第i行第j列为1,第i-1行第j列为0,那么说明第i行第j列应该竖放并填充第i-1行第j列,成立后向左移动一格 3.第i行第j列为0,说明不放方块,那么第i-1行第j列必须为1,否则没法填充这个格子。若第i-1行第j列也为0,不兼容不合法 (至于第i行第j列这个格子空着干什么,其实就是留出来给第i+1行竖放的时候插进来的) 那么目标状态是什么,就是dp[n][maxs],maxs表示全部是1的串,即第n-1行以上全部覆盖满,第n行的状态为maxs,即没有空着的格子,也全部覆盖满了 即整个矩形全部被覆盖满了的状态 最后是第1行的初始化问题,因为约定了“上凸型摆放”,所以第1行是不能竖放方格的,只能横放方格, 每横放一个必定占据两个格子,所以在判断一个状态(那个01串)的时候,连着的1的个数必定为偶数,如果出现了单独的1,说明不合法 */
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; typedef long long ll; ll dp[12][1<<11],ans[12][12]; int n,m; bool cmp1(int x) { for(int i=0; i<m;) if(x&(1<<i)) { if(i==m-1) return 0; if(x&(1<<(i+1))) i+=2; else return 0; } else i++; return 1; } bool cmp2(int a,int b) { for(int i=0; i<m;) if(a&(1<<i)) { if(b&(1<<i)) { if(i==m-1)return 0; if(b&(1<<(i+1))&&a&(1<<(i+1))) i+=2; else return 0; } else i++; } else { if(b&(1<<i))i++; else return 0; } return 1; } ll DP() { memset(dp,0,sizeof(dp)); for(int i=0; i<1<<m; i++) if(cmp1(i)) dp[1][i]=1; for(int i=2; i<=n; i++) for(int j=0; j<1<<m; j++) for(int k=0; k<1<<m; k++) if(cmp2(j,k)) dp[i][j]+=dp[i-1][k]; return ans[n][m]=ans[m][n]=dp[n][(1<<m)-1]; } int main() { memset(ans,-1,sizeof(ans)); while(~scanf("%d%d",&n,&m)&&m+n) { if(m>n)swap(n,m); if(ans[n][m]!=-1) { cout<<ans[n][m]<<endl; continue; } if((m*n)%2==1) { printf("0\n"); ans[n][m]=ans[m][n]=0; continue; } cout<<DP()<<endl; } return 0; }
持续更新博客地址:
blog.csdn.net/martinue