[Luogu P2566] [BZOJ 1294] [SCOI2009]围豆豆

14 篇文章 0 订阅
12 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近MOKIA手机上推出了一种新的围豆豆游戏,大家一起来试一试吧。

游戏的规则非常简单,在一个 N × M N×M N×M的矩阵方格内分布着 D D D颗豆子,每颗豆有不同的分值 V i V_i Vi。游戏者可以选择任意一个方格作为起始格,每次移动可以随意的走到相邻的四个格子,直到最终又回到起始格。最终游戏者的得分为所有被路径围住的豆豆的分值总和减去游戏者移动的步数。矩阵中某些格子内设有障碍物,任何时刻游戏者不能进入包含障碍物或豆子的格子。游戏者可能的最低得分为 0 0 0,即什么都不做。

注意路径包围的概念,即某一颗豆在路径所形成的多边形(可能是含自交的复杂多边形)的内部。下面有两个例子:

img

第一个例子中,豆在路径围成的矩形内部,所以豆被围住了。第二个例子中,虽然路径经过了豆的周围的 8 8 8个格子,但是路径形成的多边形内部并不包含豆,所以没有围住豆子。

布布最近迷上了这款游戏,但是怎么玩都拿不了高分。聪明的你决定写一个程序来帮助他顺利通关。

输入输出格式

输入格式:

第一行两个整数 N N N M M M,为矩阵的边长。

第二行一个整数 D D D,为豆子的总个数。

第三行包含 D D D个整数 V 1 V_1 V1 V D V_D VD,分别为每颗豆子的分值。

接着 N N N行有一个 N × M N×M N×M的字符矩阵来描述游戏矩阵状态,0表示空格,#表示障碍物。而数字 1 1 1 9 9 9分别表示对应编号的豆子。

输出格式:

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

输入输出样例

输入样例#1:
3 8
3
30 -100 30
00000000
010203#0
00000000
输出样例#1:
38

说明

50%的数据满足 1 ≤ D ≤ 3 1≤D≤3 1D3

100%的数据满足 1 ≤ D ≤ 9 , 1 ≤ N , M ≤ 10 , − 10000 ≤ V i ≤ 10000 1≤D≤9,1≤N, M≤10,-10000≤V_i≤10000 1D91N,M1010000Vi10000

解题分析

数据范围这么小, 直接大力状压围到点的状态就好了。

问题在于我们怎么判是否围住了, 有个结论: 从给定点向多边形任意一个方向引一条射线, 如果只与奇数条边相交, 那么一定是在多边形内的。

但在这里有点麻烦: 如果我们水平射出射线, 水平的边也会交到。 我们可以强行规定从上往下的边才产生贡献, 并且是从相同的行数向靠下面的一行转移或从下面一行向当前行转移才记录次数。

这样状压之后枚举起点跑个SPFA就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <queue>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};
int n, m, all, bean, ans;
int dis[11][11][512];
bool inq[11][11][512];
struct INFO {int x, y, s;};
std::queue <INFO> q;
char mp[11][11];
int px[11], py[11], val[11], sum[512];
IN bool avai(R int x, R int y) {return x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] == '0';}
IN int GetS(R int curx, R int cury, R int nx, R int S)
{
	for (R int i = 0; i < bean; ++i)
	if (((curx == px[i] && nx == px[i] + 1) || (curx == px[i] + 1 && nx == px[i])) && cury > py[i]) S ^= 1 << i;
	return S;
}
IN void SPFA(R int sx, R int sy)
{
	std::memset(dis, 63, sizeof(dis));
	dis[sx][sy][0] = 0; INFO now;
	q.push({sx, sy, 0});
	R int nx, ny, ns;
	W (!q.empty())
	{
		now = q.front(); q.pop();
		for (R int i = 0; i < 4; ++i)
		{
			nx = now.x + dx[i], ny = now.y + dy[i];
			if (!avai(nx, ny)) continue;
			ns = i < 2 ? GetS(now.x, now.y, nx, now.s) : now.s;
			if (dis[nx][ny][ns] > dis[now.x][now.y][now.s] + 1)
			{
				dis[nx][ny][ns] = dis[now.x][now.y][now.s] + 1;
				if (!inq[nx][ny][ns]) inq[nx][ny][ns] = true, q.push({nx, ny, ns});
			}
		}
		inq[now.x][now.y][now.s] = false;
	}
	for (R int i = 1; i < all; ++i) ans = std::max(ans, sum[i] - dis[sx][sy][i]);
}
int main(void)
{
	scanf("%d%d%d", &n, &m, &bean); all = 1 << bean;
	for (R int i = 0; i < bean; ++i) scanf("%d", &val[i]);
	for (R int i = 1; i < all; ++i)
	{
		for (R int j = 0; j < bean; ++j)
		if ((i >> j) & 1) sum[i] += val[j];
	}
	for (R int i = 1; i <= n; ++i)
	{
		scanf("%s", mp[i] + 1);
		for (R int j = 1; j <= m; ++j)
		{
			if (isdigit(mp[i][j]))
			{
				if (mp[i][j] != '0')
				{
					px[mp[i][j] - '0' - 1] = i;
					py[mp[i][j] - '0' - 1] = j;
				}
			}
		}
	}
	for (R int i = 1; i <= n; ++i)
	for (R int j = 1; j <= m; ++j)
	if (mp[i][j] == '0') SPFA(i, j);
	printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值