[状态压缩 dp] [CEOI 2002] Bugs Integrated, Inc.

Description
Bugs 公司是一个生产芯片的公司。他们在生产 6T 的 Q-RAM 芯片。每个芯片包含 6 个单位正方形,形成一个 2*3 的矩形。现在有一块一个 n*m 的矩形单晶硅,上面所有的格子都被测试过,坏的标记为黑色。最后,这块 n*m 的单晶硅要切出许多 2*3 的芯片。当然,任意一个芯片不能包括坏的格子。有可能不存在一种切法使得所有的好的格子都是芯片的一部分。公司想要尽量少的浪费。所以他们想知道这块单晶硅最多能切出多少个芯片。Input第一行包括一个整数 D 表示测试组数。对于每组测试数据,第一行三个整数 n、m、k (1 <= n <= 150, 1 <= m <= 10, 0 <= k <= nm)。k 表示坏的格子的数目。接下来 k 行,每行两个数 x、y 表示坏的格子的坐标。1 <= x <= n,1 <= y <= m。Output对于最组测试数据,输出最多能切出多少个芯片。
这一看就是一个状压 dp 对吧。不过貌似需要压两行,因为所放的芯片影响范围有三行(按行转移)。
状态就是 f[i][j][k], 表示第 i 行状态为 j,第 i + 1 行状态为 k 的最大值。然后按行转即可。
其实重点是一个小优化(但是有很大的作用):
在原来做广场铺砖之类的题目按行转移时,总是要把所有的状态 for 一遍。既然在这里压了两行,则 for 一遍要 1000000 次,这显然会非常慢。显然只需要保存有用的状态即可,所以用一个栈保存,速度翻了 4 倍多。
Code :
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;

typedef const int & intr;

struct pairy
{
	int s, t;
	pairy() {s = t = 0;}
	pairy(intr x, intr y) {s = x, t = y;}
}	sta1[100000], sta2[100000], * s1 = sta1, * s2 = sta2, * tot, * up;

int T, n, m, k, lim, l, ans, tt, kk;
int ff[2][1024][1024];
int gg[2][1024][1024];
int (* f)[1024], (* f1)[1024];
int (* g)[1024], (* g1)[1024];
bool b[151][11];

void show(int a)
{
	for (int i = 1; i <= m; ++ i)
		if (a) fprintf(stderr, "%d", a & 1), a >>= 1;
		else fputs("0", stderr);
	fputs("\n", stderr);
}

bool chk1(intr x, intr y, intr z, intr p)
{
	if (l + 2 > n || p + 1 > m) return 0;
	for (int i = p; i < p + 2; ++ i)
		if (x >> i - 1 & 1 || y >> i - 1 & 1 || z >> i - 1 & 1 || b[l][i] || b[l + 1][i] || b[l + 2][i]) return 0;
	return 1;
}

bool chk2(intr x, intr y, intr p)
{
	if (p + 2 > m) return 0;
	for (int i = p; i < p + 3; ++ i)
		if (x >> i - 1 & 1 || y >> i - 1 & 1 || b[l][i] || b[l + 1][i]) return 0;
	return 1;
}

void dfs(int x, int y, int z, int p, int s)
{
	if (p > m)
	{
		if (g[y][z] != tt + 1) f[y][z] = 0, g[y][z] = tt + 1, * (++ tot) = pairy(y, z);
		ans = max(ans, f[y][z] = max(f[y][z], kk + s));
		return;
	}
	if (chk1(x, y, z, p))
		dfs(x | 3 << p - 1, y | 3 << p - 1, z | 3 << p - 1, p + 2, s + 1);
	if (chk2(x, y, p))
		dfs(x | 7 << p - 1, y | 7 << p - 1, z, p + 3, s + 1);
	for (++ p; (x >> p - 1 & 1 || b[l][p]) && p <= m; ++ p);
	dfs(x, y, z, p, s);
}

int main()
{
	freopen("bugs.in", "r", stdin);
	freopen("bugs.out", "w", stdout);
	
	for (scanf("%d", & T); T --; )
	{
		scanf("%d%d%d", & n, & m, & k), lim = 1 << m;
		memset(b, ans = 0, sizeof b);
		for (int i = 1, x, y; i <= k; ++ i) scanf("%d%d", & x, & y), b[x][y] = 1;
		g1 = gg[0], g = gg[1], f1 = ff[0], f = ff[1];
		g1[0][0] = ++ tt, f1[0][0] = 0, tot = s1, * (++ tot) = pairy(0, 0);
		for (l = 1; l < n; ++ l)
		{
			swap(s1, s2), up = tot, tot = s1;
			for (pairy * i = s2 + 1; i <= up; ++ i)
				kk = f1[i->s][i->t], dfs(i->s, i->t, 0, 1, 0);
			swap(g1, g), swap(f1, f), ++ tt;
		}
		printf("%d\n", ans);
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值