有一个n*m的棋盘,现在用1*2的骨牌去覆盖,问有多少种不同方法可以将这个棋盘全部覆盖。
1、状态压缩DP
枚举相邻两行的状态进行匹配转移 复杂度O( n * (2^m)^2 )
poj 2663:http://poj.org/problem?id=2663
#include <cstdio> // poj 2663.cpp
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod %100000007
int n;
LL dp[35][8];
bool fit(int x1 , int x2 , int uplim)
{
if ( (x1 | x2) != uplim ) return false;
int t = x1 & x2;
if ( t == 0 || t == 3 || t == 6 ) return true;
return false;
}
int main()
{
while( ~scanf("%d",&n) )
{
if ( n == -1 ) break;
if ( n == 0 )
{
puts("1");
continue;
}
if ( n & 1 )
{
puts("0");
continue;
}
Clean(dp,0);
dp[1][0] = dp[1][3] = dp[1][6] = 1;
int uplim = 1<<3;
rep(i,2,n)
rep(j,0,uplim-1)
rep(k,0,uplim-1)
if( fit(j,k,uplim-1) ) dp[i][j]+=dp[i-1][k];
cout<<dp[n][uplim-1]<<endl;
}
return 0;
}
2、矩阵转移
当n和m有一个值很大 而另一个值很小(最大在7左右),我们就可以构造一个状态转移矩阵进行两行之间的转移,再用矩阵快速幂加速一下。具体hihocoder上讲的比较详细。
hihocoder 1151 1161
poj 3420: http://poj.org/problem?id=3420
<span style="font-size:24px;"><strong>#include <cstdio> //hiho 1161.cpp
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 12357
int uplim;
int n,k;
struct node
{
int a[1<<7][1<<7];
void E()
{
Clean(a,0);
rep(i,0,uplim) a[i][i] = 1;
}
void zhuanyi()
{
Clean(a,0);
dfs(0,0,0);
}
void dfs( int x , int y , int col ) //构造上一行状态为x,当前行状态为y x->y的转移矩阵
{
if ( col == k )
{
a[x][y] = 1;
return;
}
dfs( x<<1|1 , y<<1 , col + 1 ); //第col列不放,所以上一行已经被填充过了,而当前行还是空的
dfs( x<<1 , y<<1|1 , col + 1 );//col列竖着放,上一行肯定没有被填充,而放过之后当前行会被填充
if ( col + 2 <= k ) dfs( x<<2|3 , y<<2|3 , col + 2 );//在当前行的col列和col+1列横着放
}
};
node multi( node &x , node &y ) //矩阵乘法
{
node ans;
rep(i,0,uplim)
rep(j,0,uplim)
{
ans.a[i][j] = 0;
rep(k,0,uplim)
ans.a[i][j] = ( ans.a[i][j] + x.a[i][k]*y.a[k][j] ) % mod;
}
return ans;
}
int main()
{
cin>>n>>k;
if ( n < k ) swap(n,k);
uplim = (1<<k)-1;
node temp,ans;
ans.E();
temp.zhuanyi();
while( n )
{
if ( n & 1 ) ans = multi( ans , temp );
temp = multi( temp , temp );
n>>=1;
}
cout<<ans.a[uplim][uplim]%mod<<endl;
return 0;
}</strong></span>
3、扫描线
复杂度 O( n*m*2^m )
uva 11270
#include <cstdio> //uva 11270.cpp
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod %100000007
const int maxn = 13;
LL dp[2][1<<maxn];
int n,m;
void update( int a , int b , int cur )
{
dp[cur][b] += dp[cur^1][a];
}
void solve()
{
int cur = 0;
int uplim = (1<<m) - 1;
dp[0][uplim] = 1;
rep( i , 1 , n )
rep( j , 1 , m )
{
cur ^= 1;
Clean(dp[cur],0);
rep( k , 0 , uplim ) //枚举上一个位置的状态
{
if ( k & 1<<(m-1) ) //
update( k , (k<<1)^(uplim+1) , cur ); //不放
if ( i > 1 && !( k & 1<<(m-1) ) ) //竖着放
update( k , k<<1|1 , cur );
if ( j > 1 && ( k & 1<<(m-1) ) && !( k & 1 ) ) //横着放
update( k , (k<<1)^(uplim+1)^3 , cur );
}
}
printf("%lld\n",dp[cur][uplim]);
}
int main()
{
while( scanf("%d%d",&n,&m) == 2 )
{
if ( n < m ) swap(n,m);
Clean(dp,0);
solve();
}
return 0;
}
3、状态压缩转移
枚举相邻两行的状态进行匹配转移 复杂度O( n * (2^m)^2 )
poj 2663
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod %100000007
int n;
LL dp[35][8];
bool fit(int x1 , int x2 , int uplim)
{
if ( (x1 | x2) != uplim ) return false;
int t = x1 & x2;
if ( t == 0 || t == 3 || t == 6 ) return true;
return false;
}
int main()
{
while( ~scanf("%d",&n) )
{
if ( n == -1 ) break;
if ( n == 0 )
{
puts("1");
continue;
}
if ( n & 1 )
{
puts("0");
continue;
}
Clean(dp,0);
dp[1][0] = dp[1][3] = dp[1][6] = 1;
int uplim = 1<<3;
rep(i,2,n)
rep(j,0,uplim-1)
rep(k,0,uplim-1)
if( fit(j,k,uplim-1) ) dp[i][j]+=dp[i-1][k];
cout<<dp[n][uplim-1]<<endl;
}
return 0;
}