前言:有这么一道动态规划的好题,我觉得值得纪念一下把他弄懂了
思路:
使用动态规划。由于N非常大,所以选择&来压缩空间。原理是:
奇数&1=1
偶数&1=0
我定义的dp是这样:
- dp[i][0]表示第i列格子的积木块为0
- dp[i][1]表示第i列的积木块数量是1,且当前列的下面一个格子是空的
- dp[i][2]表示第i列的积木块数量是1,且当前列的上面一个格子是空的
- dp[i][3]表示第i列的积木块数量是2,也就是第i列被填满。
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
long long int f[2][4];
int main()
{
int n;
cin>>n;
f[1][0]=1; //第一层没有格子,那么只有什么都不放一种情况
f[1][1]=0; //第一层不可能只有一个积木,因为L型积木至少两层才可以使用
f[1][2]=0; //同理;
f[1][3]=1; //第1层填满,就是放一个竖的积木
for(int i=2;i<=n;i++)
{
f[i&1][0]=f[(i-1)&1][3]; //第i层没有积木,等价于第i-1层积木填满
f[i&1][1]=(f[(i-1)&1][0]+f[(i-1)&1][2])%mod; //第i层上面有积木,下面没有积木,等于:第i-1层没有积木的情况放一个L+第i-1层有一个L的情况放一个横条
f[i&1][2]=(f[(i-1)&1][0]+f[(i-1)&1][1])%mod; //同理
f[i&1][3]=((f[(i-1)&1][3]+f[(i-1)&1][0])%mod+(f[(i-1)&1][1]+f[(i-1)&1][2])%mod)%mod; //第i层填满的情况等于:(1)第i-1层填满 加一个竖条
//(2)第i-1层空的,加两个横条 (3)第i-1层上面一个格子+1个L (4)第i-1层下面一个格子+1个L。
}
cout<<f[n&1][3]<<endl;
return 0;
}
说实话,这道题不难,就是看了别人的题解把自己绕晕了,还没看懂。
然后有大神用一个递推方程写出一个更好的方法:
我来试写一次:
设f[n]为前n层填满的方案:
那么穷举一下所有的可能
- f[i]+=f[i-1] 即第i-1层是填满的,第i层加一个竖条
- f[i]+=f[i-2] 即第i-2层是填满的,然后加两个横条填满第i-1和第i层
- f[i]+=f[i-3]*2 第i-1层放了一个L,L可以有两种放法
- f[i]+=f[i-4]*2 第i-3层放一个L,然后i-2层接一个竖条
- f[i]+=f[i-5]2 …
得出递推式:
f[i]=2f[i-1]+f[i-3];
#include<iostream>
using namespace std;
const int mod=1e9+7,N=1e7+10;
long long int f[N];
int main()
{
int n;
cin>>n;
f[0]=0;
f[1]=1;
f[2]=2;
f[3]=5;
for(int i=4;i<=n;i++)
{
f[i]=(2*f[i-1]+f[i-3])%mod;
}
cout<<f[n];
return 0;
}