POJ1038 Bugs Integrated, Inc.

题意:在一个有坏点的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;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值