骨牌覆盖,一开始写了插头DP。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define pp 1000000007 using namespace std; long long dp[3][6][65]; int n,m; int main() { scanf("%d %d",&n,&m); dp[0][m][0]=1; for(int i=1;i<=n;i++) { memset(dp[1],0,sizeof(dp[1])); for(int sta=0;sta<(1<<m);sta++) { dp[1][0][sta<<1]=dp[0][m][sta]; } for(int j=1;j<=m;j++) for(int sta=0;sta<(1<<(m+1));sta++) { int x=1<<(j-1); int y=1<<j; if((sta&x)==0&&(sta&y)==0) dp[1][j][sta]=(dp[1][j-1][sta+x]+dp[1][j-1][sta+y])%pp; if((sta&x)==0&&(sta&y)!=0) dp[1][j][sta]=dp[1][j-1][sta-y]; if((sta&x)!=0&&(sta&y)==0) dp[1][j][sta]=dp[1][j-1][sta-x]; //cout<<i<<' '<<j<<' '<<sta<<' '<<dp[i][j][sta]<<endl; } for(int j=1;j<=m;j++) for(int sta=0;sta<(1<<(m+1));sta++) { dp[0][j][sta]=dp[1][j][sta]; } } cout<<dp[1][m][0]<<endl; }
果断T掉,后来知道动态规划的原理就是把状态看成点,转移看成边,就是一个拓扑图。而这道题它的拓扑图结构比较清晰,要求的方案数可以用矩阵乘法来做,但是插头DP的状态与行列的值有关,好像不能这么搞。要用状压系列的(晕~~)。
代码转自http://blog.csdn.net/naipp/article/details/52815127
#include<bits/stdc++.h> using namespace std; #define mod 1000000007 #define PI acos(-1.0) #define INF 0x3f3f3f3f typedef long long LL; typedef unsigned long long ULL; typedef vector<LL> vec; typedef vector<vec> mat; // A*B mat mul(mat& A, mat& B) { mat C(A.size(), vec(B[0].size())); for(int i = 0; i < (int)A.size(); ++i) for(int j = 0; j < (int)B[0].size(); ++j) for(int k = 0; k < (int)B.size(); ++k) C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod; return C; } // A^n mat pow(mat A, int n) { mat B(A.size(), vec(A.size())); for(int i = 0; i < (int)A.size(); ++i) B[i][i] = 1; while(n) { if(n & 1) B = mul(B, A); A = mul(A, A); n >>= 1; } return B; } int n,m; LL dp[1<<5][1<<5]; void dfs(int c,int pre,int now){ if(c>n)return ; if(c==n){ dp[pre][now]++; return ; } dfs(c+1,pre<<1,now<<1|1); dfs(c+1,pre<<1|1,now<<1); dfs(c+2,pre<<2,now<<2); } int main() { cin>>m>>n; mat a(1<<n,vec(1<<n)); dfs(0,0,0); for(int i=0;i<(1<<n);i++){ for(int j=0;j<(1<<n);j++){ a[i][j]=dp[i][j]; //cout<<a[i][j]; } //cout<<endl; } a=pow(a,m+1); printf("%lld ",a[0][(1<<n)-1]); }