设扩展到了
[
i
]
[
j
]
[
s
]
[i][j][s]
[i][j][s],即第
i
i
i 行第
j
j
j 例,且上一行的状态是
s
s
s
新状态的前
j
+
1
j+1
j+1位已经确定,而后
m
−
j
−
1
m-j-1
m−j−1 位则尚未确定,但必定会由
s
s
s 的后
m
−
j
−
1
m-j-1
m−j−1 位决定
显然,若新状态的前
j
+
1
j+1
j+1位 及 原状态
s
s
s 的后
m
−
j
−
1
m-j-1
m−j−1 位 一致时,将产生相同的结果,因此合并这些项将减少运算量
合并后,状态量是m位二进制,因此每扩展一块,将得到
2
m
2^m
2m 种新状态
优化后,复杂度为:
O
(
n
⋅
m
⋅
2
m
)
O(n\cdot m \cdot 2^m)
O(n⋅m⋅2m)
dp数组优化
维护上次扩展结果
维护本次扩展结果
显然,滚动数组即可,
p
r
e
[
s
]
pre[s]
pre[s] 和
c
u
r
[
s
]
cur[s]
cur[s], 表示扩展结果是
s
s
s 的方案的可能数量
其它优化
地面
n
⋅
m
n\cdot m
n⋅m 为奇数则无解
滚动数组的数据清除采用使用后即清除,不采用统一的
m
e
m
s
e
t
memset
memset 方式
代码
/* 铺地砖 状压dp */#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint md =1e9+7;constint MXS =(1<<17)+5;
ll dp[MXS<<1],*pre,*cur;intmain(){int t, S, N, M;scanf("%d",&t);while(t--){scanf("%d%d",&N,&M);if(M >17) M ^= N, N ^= M, M ^= N;// 交换if(N&1&& M&1){printf("0\n");continue;}
S =(1<<M)-1;
pre = dp, cur = dp+MXS;memset(dp,0,sizeof dp), pre[0]=1;// 设定初态for(int n =0; n < N;++n){for(int m =0; m < M;++m){for(int s =0; s <= S; pre[s++]=0){if(pre[s]==0)continue;// 未出现的状态if(s&(1<<m)){// 向上铺
cur[s&~(1<<m)]=(cur[s&~(1<<m)]+ pre[s])%md;continue;}
cur[s|(1<<m)]=(cur[s|(1<<m)]+ pre[s])%md;// 不铺if(s&(1<<m-1)&& m >0)// 向左铺
cur[s&~(3<<m-1)]=(cur[s&~(3<<m-1)]+ pre[s])%md;}swap(pre, cur);}}printf("%d\n", pre[0]);}return0;}
目录问题分析代码问题求用 1×21\times 21×2 的地砖铺设 n×mn\times mn×m 的地面的方案数.n×m≤300n\times m \leq 300n×m≤300结果对 1e9+71e9+71e9+7 取模分析代码#include<bits/stdc++.h>using namespace std;#define d(i,j) dp[i][j]typedef long long ll;const int md = 1e9+7;const in