题意:在一个有坏点的n*m(1 <= n <= 150, 1 <= m <= 10)的矩阵中,放置3*2的矩形,问最多能放置多少个。
分析:
观察到m的范围比较小,所以我们可以考虑用状压dp.
以前状压dp只做过TSP问题,所以状压dp还是个很薄弱的知识点,做了一下这道题后,感觉稍微明白一点了...
首先设计状态,显然,第i行能放的东西只与第i-1行和i-2行有关,我们可以考虑用三进制表示状态。
0表示i和i-1行都没有放,1表示i行没放i-1行放了,2表示i和i-1行都放了。
为了减少时间,我们使用刷表法。
设i-1行第j列状态为pj,则i行第j列状态就是pj ? pj-1 : 0,这样就实现了状态的压缩。
设f(i, j)为考虑前i行,第i行状态为j的放置的最大数量。
枚举每一行,枚举上一行的状态,计算3种决策下这行的状态即可。
别忘了用滚动数组,否则MLE.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define p(i) (p/pw[i-1]%3)
#define q(i) (q/pw[i-1]%3)
int T,n,m,k,p,q,ans,ha,pw[15],a[155][15],f[2][60000];
void dp(int x, int y, int num) {
if(y > m) return;
if(y<m&&!p(y)&&!p(y+1)&&!q(y)&&!q(y+1)) {
q += (pw[y-1]+pw[y])*2, f[ha][q] = max(f[ha][q], num+1);
dp(x, y+2, num+1);
q -= (pw[y-1]+pw[y])*2;
}
if(y<m-1&&!q(y)&&!q(y+1)&&!q(y+2)) {
q += (pw[y-1]+pw[y]+pw[y+1])*2, f[ha][q] = max(f[ha][q], num+1);
dp(x, y+3, num+1);
q -= (pw[y-1]+pw[y]+pw[y+1])*2;
}
dp(x, y+1, num);
}
int main() {
scanf("%d", &T);
pw[0] = 1;
for(int i = 1; i < 11; i++) pw[i] = pw[i-1]*3;
while(T--) {
memset(a, 0, sizeof a);
memset(f[0], -1, sizeof f[0]);
scanf("%d%d%d", &n, &m, &k);
while(k--) scanf("%d%d", &p, &q), a[p][q] = 1;
p = 0;
for(int i = 1; i <= m; i++) p += pw[i-1]*(a[1][i]+1);
ha = ans = f[0][p] = 0;
for(int i = 2; i <= n; i++) {
ha ^= 1;
memset(f[ha], -1, sizeof f[ha]);
for(p = 0; p < pw[m]; p++) if(~f[ha^1][p]) {
q = 0;
for(int j = 1; j <= m; j++)
if(a[i][j]) q += pw[j-1]*2; else q += pw[j-1]*(p(j)?p(j)-1:0);
f[ha][q] = max(f[ha][q], f[ha^1][p]), dp(i, 1, f[ha^1][p]);
}
}
for(int i = 0; i < pw[m]; i++) ans = max(ans, f[ha][i]);
printf("%d\n", ans);
}
return 0;
}