[BZOJ1294][SCOI2009]围豆豆Bean

试题描述

[BZOJ1294][SCOI2009]围豆豆Bean_#define

输入

第一行两个整数 \(N\)\(M\),为矩阵的边长。 第二行一个整数 \(D\),为豆子的总个数。 第三行包含 \(D\) 个整数 \(V_1\)\(V_D\),分别为每颗豆子的分值。 接着 \(N\) 行有一个 \(N \times M\) 的字符矩阵来描述游戏矩阵状态,0 表示空格,# 表示障碍物。而数字 19 分别表示对应编号的豆子。

输出

仅包含一个整数,为最高可能获得的分值。

输入示例

3 8
3
30 -100 30
00000000
010203#0
00000000

输出示例

38

数据规模及约定

\(50\texttt{%}\) 的数据满足 \(1 \le D \le 3\)

\(100\texttt{%}\) 的数据满足 \(1 \le D \le 9\)\(1 \le N, M \le 10\)\(-10000 \le V_i \le 10000\)

题解

传说中的射线法。判断一个点是否在一个任意多边形内部可以从这个点向任意一个方向引一条射线,若多边形的边交它奇数次则该点在多边形内,否则不在。

这道题对于每个豆豆我们向右水平引一条射线,然后状压,在经过射线的时候改变一些豆子是否在内部的状态,枚举起点 BFS 即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 15
#define maxs 512

int n, m, D, val[maxn], beans[maxn][maxn], sum[maxs];
char Map[maxn][maxn];
struct Node {
	int x, y, S; bool ud, from;
	Node() {}
	Node(int _1, int _2, int _3, bool _4, bool _5): x(_1), y(_2), S(_3), ud(_4), from(_5) {}
} Q[maxn*maxn*maxs<<2];
int step[maxn][maxn][maxs][2][2], ans, dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
bool iszero(int x, int y) { return 1 <= x && x <= n && 1 <= y && y <= m && Map[x][y] == '0'; }
void solve(int sx, int sy) {
	memset(step, -1, sizeof(step));
	int hd = 0, tl = 0;
	if(beans[sx][sy]) {
		if(sx > 1) step[sx][sy][0][0][0] = 0, Q[++tl] = Node(sx, sy, 0, 0, 0);
		if(sx < n) step[sx][sy][0][1][1] = 0, Q[++tl] = Node(sx, sy, 0, 1, 1);
	}
	else step[sx][sy][0][0][0] = 0, Q[++tl] = Node(sx, sy, 0, 0, 0);
	while(hd < tl) {
		Node u = Q[++hd];
		rep(i, 0, 3) {
			Node v(u.x + dx[i], u.y + dy[i], u.S, u.ud, u.from);
			if(iszero(v.x, v.y)) {
				if(dx[i]) {
					if(beans[u.x][u.y] && ((dx[i] == -1 && u.ud) || (dx[i] == 1 && !u.ud)))
						v.S ^= beans[u.x][u.y];
					if(beans[v.x][v.y]) v.ud = dx[i] == -1;
					else v.ud = 0;
				}
				if(step[v.x][v.y][v.S][v.ud][v.from] >= 0) continue;
				step[v.x][v.y][v.S][v.ud][v.from] = step[u.x][u.y][u.S][u.ud][u.from] + 1;
				Q[++tl] = v;
			}
		}
	}
	rep(S, 0, (1 << D) - 1) {
		if(beans[sx][sy]) {
			if(step[sx][sy][0][0][0] >= 0) {
				if(step[sx-1][sy][S][0][0] >= 0) ans = max(ans, sum[S^beans[sx-1][sy]] - step[sx-1][sy][S][0][0] - 1);
				if(step[sx-1][sy][S][1][0] >= 0) ans = max(ans, sum[S] - step[sx-1][sy][S][1][0] - 1);
			}
			if(step[sx][sy][0][1][1] >= 0) {
				if(step[sx+1][sy][S][0][1] >= 0) ans = max(ans, sum[S] - step[sx+1][sy][S][0][1] - 1);
				if(step[sx+1][sy][S][1][1] >= 0) ans = max(ans, sum[S^beans[sx+1][sy]] - step[sx+1][sy][S][1][1] - 1);
			}
		}
		else {
			if(step[sx][sy][S][0][0] >= 0) ans = max(ans, sum[S] - step[sx][sy][S][0][0]);
			if(step[sx][sy][S][1][0] >= 0) ans = max(ans, sum[S] - step[sx][sy][S][1][0]);
		}
	}
	return ;
}

int main() {
	n = read(); m = read(); D = read();
	rep(i, 1, D) val[i] = read();
	rep(i, 1, n) scanf("%s", Map[i] + 1);
	
	rep(i, 1, n) rep(j, 1, m)
		if('1' <= Map[i][j] && Map[i][j] <= '9')
			rep(k, j, m) beans[i][k] |= (1 << Map[i][j] - '1');
	
	rep(S, 0, (1 << D) - 1)
		rep(j, 0, D - 1) if(S >> j & 1) sum[S] += val[j+1];
	rep(i, 1, n) rep(j, 1, m) if(Map[i][j] == '0') solve(i, j);
	
	printf("%d\n", ans);
	
	return 0;
}

一个小技巧:每个豆子可以看成是它所在的方格的上边界的中点,从这个中点向右引射线,这样会好做很多。(想一想,为什么这样是对的)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 15
#define maxs 512

int n, m, D, val[maxn], beans[maxn][maxn];
char Map[maxn][maxn];

struct Node {
	int x, y, S;
	Node() {}
	Node(int _1, int _2, int _3): x(_1), y(_2), S(_3) {}
} Q[maxn*maxn*maxs];
int step[maxn][maxn][maxs], ans, dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
bool iszero(int x, int y) { return 1 <= x && x <= n && 1 <= y && y <= m && Map[x][y] == '0'; }
void solve(int sx, int sy) {
	memset(step, -1, sizeof(step));
	step[sx][sy][0] = 0;
	int hd = 0, tl = 0;
	Q[++tl] = Node(sx, sy, 0);
	while(hd < tl) {
		Node u = Q[++hd];
		rep(i, 0, 3) {
			Node v(u.x + dx[i], u.y + dy[i], u.S);
			if(iszero(v.x, v.y)) {
				if(beans[v.x][v.y] && dx[i] == 1) v.S ^= beans[v.x][v.y];
				if(beans[u.x][u.y] && dx[i] == -1) v.S ^= beans[u.x][u.y];
				if(step[v.x][v.y][v.S] >= 0) continue;
				step[v.x][v.y][v.S] = step[u.x][u.y][u.S] + 1;
				Q[++tl] = v;
			}
		}
	}
	rep(S, 0, (1 << D) - 1) {
		int sum = 0;
		rep(i, 0, D - 1) if(S >> i & 1) sum += val[i+1];
		if(step[sx][sy][S] >= 0) ans = max(ans, sum - step[sx][sy][S]);
	}
	return ;
}

int main() {
	n = read(); m = read(); D = read();
	rep(i, 1, D) val[i] = read();
	rep(i, 1, n) scanf("%s", Map[i] + 1);
	
	rep(i, 1, n) rep(j, 1, m)
		if('1' <= Map[i][j] && Map[i][j] <= '9')
			rep(k, j, m) beans[i][k] |= (1 << Map[i][j] - '1');
	rep(i, 1, n) rep(j, 1, m) if(Map[i][j] == '0') solve(i, j);
	
	printf("%d\n", ans);
	
	return 0;
}