https://vjudge.net/problem/UVA-11270
题意:
用1×2骨牌覆盖n×m棋牌,有多少种方法?
思路:
这道题目是典型的轮廓线DP题。
所谓轮廓线DP,就是以整行整列为状态进行动态规划时无法进行状态转移,那么此时就可以用到轮廓线,当然,这种方法只能使用在一个窄棋盘上,大了肯定是不行的,要超时!
‘
轮廓线DP就是按照从上到下,从左到右的顺序进行状态转移,每个格子用二进制来表示状态,1代表的就是覆盖,0代表未覆盖。
以本题为例,如上图,我们现在要计算 k 格子,那么与它有关的就是k4 k3 k2 k1 k0这5个格子。
现在,我们对于每个格子都有三种决策:
1、不放
不放的条件是其上面的格子必须是1(也就是k4必须是1),否则无法覆盖所有的棋盘的。
如果满足条件,那么就进行状态转移,d[cur][新状态]+=d[1-cur][旧状态],也就是d[cur][k3k2k1k0k]+=d[1-cur][k4k3k2k1k0]。
2、上放
上放的条件是其上面的格子必须是0。
3、左放
左放的条件其左边的格子必须是0。
具体的算法实现请参见代码。
注意:这道题目数组开小点,开到1<<15会TLE,11就可以了!!
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pll; 14 const int INF = 0x3f3f3f3f; 15 const int maxn=11; 16 17 int n,m; 18 int cur; 19 ll d[2][1<<maxn]; 20 21 void update(int a,int b) //a是m位的旧状态,b是m+1位的新状态 22 { 23 if(b&(1<<m)) d[cur][b^(1<<m)]+=d[1-cur][a]; //先检查新状态的轮廓线首是否为1,是1则更新状态 24 } //因为轮廓线首位1,所以b^(1<<m)就是将首位置为0,将新状态变成m位 25 26 int main() 27 { 28 while(scanf("%d%d",&n,&m)==2) 29 { 30 if(n<m) swap(n,m); 31 memset(d,0,sizeof(d)); 32 d[0][(1<<m)-1]=1; 33 cur=0; 34 for(int i=0;i<n;i++) 35 { 36 for(int j=0;j<m;j++) 37 { 38 cur^=1; 39 memset(d[cur],0,sizeof(d[cur])); 40 for(int k=0;k < (1<<m);k++) 41 { 42 update(k,k<<1); //不放,直接将二进制左移一位形成新状态 43 if(i && !(k&(1<<m-1))) update(k,(k<<1)^(1<<m)^1); //上放,要求轮廓线首为0且为非首行 44 if(j && !(k&1)) update(k,(k<<1)^3); //左放,要求轮廓线首为1,尾为0,且为非首列 45 } 46 } 47 } 48 printf("%lld\n",d[cur][(1<<m)-1]); 49 } 50 return 0; 51 }
时间: 2024-10-06 00:07:13