[luogu P3786] 萃香抱西瓜 题解

题目链接:P3786 萃香抱西瓜

题面

题目描述

萃香在一个 \(n\times m\) 的空间中,这个空间中 \(s\) 个时刻内会陆续出现一些西瓜 (Suika)

萃香力气有限,她可以拿走一部分小西瓜,但不能拿走其余的大西瓜,西瓜在一段时间内会出现在空间中,西瓜每一个时刻的出现位置都是随机的,不一定连续,也有可能不动,我们认为西瓜的移动在瞬间完成,即第 \(i\) 个时刻若西瓜出现在 \((x,y)\) ,则我们认为萃香只会在 \((x,y)\) 这个坐标遇到它。

萃香希望拿走出现的所有小西瓜,并且不被大西瓜砸中。翠香最开始时站在 \((sx, sy)\) 处,每个时刻她可以向上下左右任何一个方向走一步,或者在原地等待一个时刻,问最后至少需要移动几步。

如果萃香无法拿到所有小西瓜,算做失败,另外,即使最后萃香已经拿到了所有的小西瓜,但若在 \(s\) 个时刻结束前萃香被大西瓜砸到仍然算作失败,如果成功,输出最少移动步数,否则输出 -1

输入格式

第一行五个整数 \(n,m,s,sx,sy\) ,含义见题目描述。

第二行两个整数 \(c_1,c_2\) ,表示一共有 \(c_1\) 个西瓜,其中 \(c_2\) 个是小西瓜。

接下来 \(c_1\) 段数据,每段数据描述了一个西瓜:

第一行两个整数 \(t_1,t_2\) ,表示这个西瓜的出现世界和消失时间,特别的,若 \(t_2=s+1\) ,则表示这个西瓜不会消失。

第二行一个整数 \(op\)\(0/1\) ,如果是 \(0\) 则表示这个西瓜需要躲避,否则表示这个西瓜需要拿走。

接下来 \(t_2-t_1\) 行,第 \(i\) 行两个整数 \(x,y\) 表示在第 \(t_1+i-1\) 这个时刻这个西瓜出现在坐标 \((x,y)\) 处。

输出格式

一行一个整数表示萃香的最少移动步数或 -1

数据规模

\(1\le n,m \le 10\)
\(1\le s\le 10\)
\(1\le t1<t2\le s+1\)
\(1\le sx,x\le n\)
\(1\le sy,y\le m\)
\(1\le c_1\le 20\)
\(1\le c_2\le \min\{c_1,10\}\)
数据保证 \(op=1\) 的数据段只会出现 \(c_2\) 次。
数据保证同一时刻不会有两个西瓜出现在同一个坐标上。

题解

算法:状压 + 三维 bfs

这道题目虽然顶着紫题的名头,但是难度却只有黄题或绿题左右,思维和码量都始终,可以作为普转提的题目来刷。

通过观察数据规模,我们可以发现整个数据规模很小,地图和时间都只有 \(100\) ,要拿的西瓜只有 \(10\) 个,所以我们可以思考二进制状压存储某个西瓜有没有被拿过。

观察题面,把时间转化成一维存起来,发现这道题是一道三维最短路,其中边权只有 \(0\)(原地等待)和 \(1\)(向四个方向移动),所以可以很自然地想到双端队列 bfs 解决,接下来这道题就解决了。

复杂度分析

bfs 的复杂度取决于状态数,本题状态开了四维,分别是一维时间,两维地图,再加一维状压表示西瓜有没有拿过,但因为大西瓜阻挡等因素,不一定跑满,所以时间复杂度就是 \(\text{O}(nms\cdot2^{c_2})\) ,空间复杂度是 \(\Theta(nms\cdot2^{c_2})\)

c++ 代码
#include<bits/stdc++.h>

using namespace std;

#define Reimu inline void // 灵梦赛高
#define Marisa inline int // 魔理沙赛高
#define Sanae inline bool // 早苗赛高

typedef long long LL;
typedef unsigned long long ULL;

typedef pair<int, int> Pii;
typedef tuple<int, int, int> Tiii;
#define fi first
#define se second

const Pii D[] = {{0, 0}, {0, 1}, {0, -1}, {1, 0}, {-1, 0}};
inline Pii operator+(Pii a, Pii b) { return {a.fi + b.fi, a.se + b.se}; }

const int N = 10, M = 110;

struct Node { Pii loc; int ti, sta; };

int n, m, c, c1, c2, tot, frX, frY;
int mp[N][N][M], d[N][N][M][1 << N];

Marisa bfs() {
	if (!~mp[frX][frY][1]) return -1;
	memset(d, -1, sizeof d);
	deque<Node> Q;
	d[frX][frY][1][mp[frX][frY][1]] = 0; Q.push_back({{frX, frY}, 1, mp[frX][frY][1]});
	while (!Q.empty()) {
		auto[x, ti, sta] = Q.front(); Q.pop_front();
		if (ti == c) {
			if (sta == (1 << c2) - 1) return d[x.fi][x.se][ti][sta];
			continue;
		}
		for (auto delta: D) {
			Pii y = x + delta;
			int cost = delta.fi || delta.se;
			int Ti = ti + 1, Sta = sta | mp[y.fi][y.se][Ti];
			if (!y.fi || y.fi > n || !y.se || y.se > n || !~mp[y.fi][y.se][Ti] || ~d[y.fi][y.se][Ti][Sta]) continue;
			d[y.fi][y.se][Ti][Sta] = d[x.fi][x.se][ti][sta] + cost;
			if (cost) Q.push_back({y, Ti, Sta});
			else Q.push_front({y, Ti, Sta});
		}
	}
	return -1;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	cin >> n >> m >> c >> frX >> frY >> c1 >> c2;
	while (c1--) {
		int t1, t2, ty; cin >> t1 >> t2 >> ty;
		for (int i = t1, tag = ty ? 1 << tot++ : -1, x, y; i < t2; ++i) cin >> x >> y, mp[x][y][i] |= tag;
	}
	cout << bfs();
	return 0;
}
posted @ 2021-10-08 14:57  老莽莽穿一切  阅读(207)  评论(1编辑  收藏  举报