骨牌覆盖 状态压缩 动态规划
作者:互联网
简单一点的说这个题,就是用1x2的骨牌去覆盖NxM的棋盘,有多少种方案。N, M <= 11。
初步理解
我们对N进行初步分类讨论:
当N = 1时,M为奇,无解,M为偶,有唯一解
当N = 2时,Fibonacci数列
(自己可以找例子推一下)
深入分析
可N > 2时怎么办?
由于N<=11,不难想到状态压缩,可以用二进制位运算表示。
难点:状态表示:dp[i][S]表示到达第i行,当前覆盖状态为S的方案数。
对横着放的骨牌,两个位置均置为1
对竖着放的骨牌,上方置1,下方置0
则在种情况下,我们设上一行为s1, 下一行为s2,可转移情况:
Case 1: s1某位为0,s2对应位必须为1
Case 2: s1某位为1,s2对应位可为0,可为1,但是需满足s1&s2可被1x2覆盖
满足s1&s2可被1x2覆盖 这是一个难点理解
则对于动态规划的主要过程,有
LL solve(int k, int S) { // 到达第k行,状态为S方案数
if (dp[k][S] != -1) return dp[k][S];
if (k == 1) {
if (S == two(m)-1) return dp[k][S] = 1;
else return dp[k][S] = 0;
}
dp[k][S] = 0;
for (int i = 0; i < two(m); ++i)
if ((i|S) == two(m)-1 && Cover(i&S)) //若可以转移
dp[k][S] += solve(k-1, i);
return dp[k][S];
}
放代码
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
int h,w;
long long dp[15][(1<<11)+5];
bool check(int S)
{
for (int i=0;i<w;++i)
if(S&(1<<i))
{
if (i==w-1||!(S&(1<<(i+1))))return false;
++i;
}
return true;
}
LL solve(int k,int s)
{
if(dp[k][s]!=-1) return dp[k][s];
if(k==1)
{
if(s==(1<<w)-1) return dp[k][s]=1;
return dp[k][s]=0;
}
dp[k][s]=0;
for(int i=0;i<(1<<w);i++)
if((i|s)==((1<<w)-1) && check(i&s))
dp[k][s]+=solve(k-1,i);
return dp[k][s];
}
int main()
{
while(cin>>h>>w)
{
if(!h && !w) return 0;
memset(dp,-1,sizeof(dp));
cout<<solve(h+1,(1<<w)-1)<<endl;
}
return 0;
}
上面是递归的写法,再来说一下递推,来先预处理出所有转移。
问题来了,如何预处理?
0和1的表示意义不变,
对于第 i 行与第 i+1 行,其中某段格子状态如下:
转移一
i: 0 i+1: 1 // 如果第 i 行的某个格子为0,那么第 i+1 行对应的一定是1才行。
转移二
i: 1 // 第 i 行的这个格子为1,有两种可能:纵向放置骨牌的下面那个;或是横向放置骨牌的第二个
i+1: 0 // 不管上面那个格子是如何放置,第i+1行都可以是0,即竖向摆放骨牌的第一个格子。
转移三
i: 1 1 i+1: 1 1 // 第 i 行和第 i+1 行都是横向排列
dfs(i,j,k)表示第i行时,上一行是j状态,下一行是k状态。
dfs(col+1, (s1<<1)|1, s2<<1);上面是1,下面是0,竖放骨牌。
dfs(col+1, s1<<1, (s2<<1)|1);上面是0,下面是1,分属于两个骨牌。
dfs(col+2, (s1<<2)|3, (s2<<2)|3);上面是11,下面是11,上面是横放骨牌。
核心代码
void dfs(int col, int s1, int s2)
{
if (col >= m) {
if (col == m) g[s2].push_back(s1);
return;
}
dfs(col+1, (s1<<1)|1, s2<<1);
dfs(col+1, s1<<1, (s2<<1)|1);
dfs(col+2, (s1<<2)|3, (s2<<2)|3);
}
放代码
#include<vector>
#include<iostream>
#include<cstring>
using namespace std;
int h,w;
vector<long long> v[(1<<11)+5];
long long f[11+5][(1<<11)+5];
void dfs(int step,int up,int down)
{
if(step>=w)
{
if(step==w) v[down].push_back(up);
return ;
}
dfs(step+1,(up<<1)|1,down<<1);
dfs(step+1,up<<1,(down<<1)|1);
dfs(step+2,(up<<2)|3,(down<<2)|3);
}
int main()
{
while(cin>>h>>w)
{
if(!h&&!w) return 0;
memset(f,0,sizeof(f));
memset(v,0,sizeof(v));
if(h<w) swap(h,w);
dfs(0,0,0);
f[0][0]=1;
for(int i=0;i<=h;i++)
for(int j=0;j<(1<<w);j++)
for(int k=0;k<v[j].size();k++)
f[i+1][j]+=f[i][v[j][k]];
cout<<f[h+1][(1<<w)-1]<<endl;
}
}
标签:return,覆盖,int,压缩,dfs,col,骨牌,s1,dp 来源: https://blog.csdn.net/weixin_45707147/article/details/102759018