地址:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1033
思路:状态压缩DP+矩阵快速幂
对于每行最多只有5列,因此可以枚举出它们的全部状态 0->(1<<m)-1,对于状态其二进制1表示已经有骨牌覆盖,0表示没有,
1.由于是1X2的骨牌,则二进制数11,110是合法的,1,10是不合法的,那么其合法的状态只有d{0,3,6,12,15,24,27,30}
2.对于第i行的状态只与第i-1行的状态有关,那么对于第i-1行的状态a中二进制位为0的就必须要在第i行的状态b中补上,
例如a=5(101) 那么b=3(11)是合法的, b=1是不合法的,因为b=1,101和001是不可以将中间的0给补上,而b=3则是将 2*1的骨牌插在中间,因此对于第i行状态b来说 a|b=(1<<m)-1才是合法的
3.同时 a&b必须在合法状态中才行,因为a&b表示第i行的1*2的骨牌的摆放状态,例如 二进制 a=1011,b=1110,这时 a&b=1010而此时的骨牌是没法摆放的
4.那么此时就可以写出状态压缩DP了
dp[1][0]=dp[1][3]=dp[1][6]=dp[1][12]=dp[1][15]=dp[1][24]=dp[1][27]=dp[1][30]=1;
int s=(1<<m)-1;
for(int i=2;i<=n;++i)
for(int j=0;j<=s;++j)
for(int k=0;k<=s;++k)
if((j|k)==s&&d[j&k])
dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;
时间复杂度为 O(n*pow(2,5)*pow(2,5))是肯定超时的
而我们对转移方程观察发现
dp[i][j]只与 dp[i-1][k]有关,而对于判断条件 if((j|k)==s&&d[j&k]) 也是固定的
那么就可以用矩阵快速幂来优化.
Code 状态压缩DP版本:
#include<iostream>
#include<cstring>
using namespace std;
const int MOD=1e9+7;
const int MAX_N=1005;
int n,m;
bool d[35];
int dp[MAX_N][35];
int main()
{
ios::sync_with_stdio(false);
d[0]=d[3]=d[6]=d[12]=d[15]=d[24]=d[27]=d[30]=1;
while(cin>>n>>m){
memset(dp,0,sizeof(dp));
dp[1][0]=dp[1][3]=dp[1][6]=dp[1][12]=dp[1][15]=dp[1][24]=dp[1][27]=dp[1][30]=1;
int s=(1<<m)-1;
for(int i=2;i<=n;++i)
for(int j=0;j<=s;++j)
for(int k=0;k<=s;++k)
if((j|k)==s&&d[j&k])
dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;
cout<<dp[n][s]<<endl;
}
return 0;
}
Code 状态压缩DP+矩阵快速幂优化:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int MOD=1e9+7;
const int MAX_S=35;
struct Matrix{
LL a[MAX_S][MAX_S];
Matrix (){ memset(a,0,sizeof(a));}
Matrix operator*(const Matrix &A){
Matrix B=Matrix();
for(int k=0;k<MAX_S;++k)
for(int i=0;i<MAX_S;++i)
for(int j=0;j<MAX_S;++j)
B.a[i][j]=(B.a[i][j]+a[i][k]*A.a[k][j])%MOD;
return B;
}
};
int n,m;
int d[MAX_S];
int main()
{
ios::sync_with_stdio(false);
d[0]=d[3]=d[6]=d[12]=d[15]=d[24]=d[27]=d[30]=1;
while(cin>>n>>m){
int s=(1<<m)-1;
Matrix res=Matrix(),A=Matrix();
for(int i=0;i<=s;res.a[i][i]=1,++i)
for(int j=0;j<=s;++j)
if((i|j)==s&&d[i&j])
A.a[i][j]=1;
while(n){
if(n&1) res=res*A;
A=A*A;
n>>=1;
}
cout<<res.a[s][s]<<endl;
}
return 0;
}