标签:
题目链接:传送门
题目大意:用1*2大小的砖块去铺满n*m大小的地面,有多少种方案
题目思路:因为1<=n,m<=11,并且砖块是1*2,故可以用二进制思想,也就是状态压缩DP,其中矩阵中为0的元素表示当前位置竖着放一块砖,而连着
两个1表示横着放一块砖(状态压缩真的很奇妙)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> #include <cstring> #include <stack> #include <cctype> #include <queue> #include <string> #include <vector> #include <set> #include <map> #include <climits> #define lson root<<1,l,mid #define rson root<<1|1,mid+1,r #define fi first #define se second #define seg int root,int l,int r #define ping(x,y) ((x-y)*(x-y)) #define mst(x,y) memset(x,y,sizeof(x)) #define mcp(x,y) memcpy(x,y,sizeof(y)) #define Min(x,y) (x<y?x:y) #define Max(x,y) (x>y?x:y) using namespace std; #define gamma 0.5772156649015328606065120 #define MOD 1000000007 #define inf 0x3f3f3f3f #define N 180000 #define maxn 1000050 typedef long long LL; typedef pair<int,int> PII; long long dp[1<<11][11]; int n,m; int check(int x){ while(x){ if(x&1){ x>>=1; if(x&1) x>>=1; else return 0; } else x>>=1; } return 1; } int _solve(int x,int y){ ///这个函数算是比较核心 for(int i=0;i<m;++i){ ///判断状态j,k是否正确 if(x&(1<<i)){ if(y&(1<<i)){ ///如果上一行和当前行是横着放砖,则必须连着出现两个1 if(i+1<m&&(x&(1<<i+1))&&(y&(1<<i+1)))++i; else return 0; } ///如果当前行竖着放,则满足条件,无需再判断 } else{ ///如果上一行是竖着放砖,则当前行必须对应为1 if(!(y&(1<<i))) return 0; } } return 1; } int main(){ int i,j,x; while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){ mst(dp,0); if(n<m)swap(n,m); ///这里交换算是一点优化,减少枚举次数 int all=1<<m; for(i=0;i<all;++i) if(check(i)) ///因为是第一行,所以必须满足条件 dp[i][0]=1; ///出现1必须连续出现两次(横着放) for(i=1;i<n;++i) for(j=0;j<all;++j) if(dp[j][i-1]) ///判断上一行是否出现过状态j,出现过才有向下推的必要 for(int k=0;k<all;++k) if(_solve(j,k)) dp[k][i]+=dp[j][i-1]; printf("%lld\n",dp[all-1][n-1]); } return 0; }
标签:
原文地址:http://www.cnblogs.com/Kurokey/p/5499852.html