1 条题解
-
0
自动搬运
来自洛谷,原作者为

Sol1
博客:sol1.netlify.app搬运于
2025-08-24 22:36:33,当前版本为作者最后更新于2023-05-31 21:30:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
整体想法其实很直接:
- 玩家记录下来自己已经看到的所有位置的信息,然后尝试利用这些信息来推断出自己在地图的哪些位置。
- 假设玩家可以推断出自己的起点是所有起点的一个子集 里面的某一个。
- 玩家为了尝试获得更多信息来具体推断自己的起点在 里面的哪一个位置,需要向外找安全的位置扩展。具体来说,需要找到一个格子满足:
- 它与玩家已经走到过的格子相邻;
- 假设这个格子与起点的相对位置是 。玩家为了保证自己不被炸到,需要保证对于 里面的任意一个起点,均满足与该起点的相对位置是 的格子是安全的。后续我们称这样的格子对于 绝对安全。
- 当玩家无法找到这样的格子时,游戏结束,玩家获得自己能走到的范围内的所有金币。
- 答案即为所有游戏结束的情况中,玩家获得金币数的最小值。
当然如果直接实现上述过程肯定写起来非常阴间,而且复杂度不一定对。
因此我们简化一下这个过程。注意到 一旦变化为 ,则新的 一定包含于 。因此对于 绝对安全的格子对于 也一定绝对安全。所以可以让玩家在此时直接丢弃所有信息重新开始扩展,也可以求得答案。
从而我们实现如下过程:
- 假设目前起点集合是 。
- 从起点开始 BFS 扩展,需要满足扩展到的点对于 绝对安全。
- 当无法扩展时,枚举已经看到的所有的点(即扩展到的点和它的外面一圈)。
- 如果某一个位置(设其相对起点的位置是 )满足:存在 ,使得相对 的位置是 的位置 和相对 的位置是 的位置 在玩家看起来不相同,则可以依照这个点的视野差异分裂 并递归进行该过程,并返回递归的两个分叉的返回值的最小值。
- 如果无法分裂 ,统计扩展到多少个金币,返回该值。
最终结果就是对所有起点构成的集合进行上述过程的返回值。
直接实现,复杂度 :每次 BFS 扩展复杂度 ,同时集合分裂 次。
这个复杂度无法通过本题。考虑到瓶颈有两个,分别是:
- BFS 扩展时判断一个点是否绝对安全,这可以压位:对于每一个 保存一个 64 位整数,第 位存储相对第 个起点的位置是 的位置是否安全(“安全”指不是
X和#),然后就可以用一次位运算判断。 - 判断一个点是否可以分裂 。这同样可以压位:一个点有三种视觉效果(障碍、空地、金币),对于每一个 保存三个 64 位整数,第 位存储相对第 个起点的位置是 的位置的视觉效果是不是障碍(空地、金币),然后就可以用三次位运算判断。
进行如上优化后,一轮过程的复杂度下降至 ,总复杂度即下降至 即可通过。
const int Next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; const int Sight[9][2] = {{0, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; char mp[405][405], rel[60][815][815]; int n, m, x[60], y[60], s; long long saf[815][815], sig[815][815][3]; bool vis[815][815], isg[815][815]; inline void Read() { cin >> n >> m; for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { cin >> mp[i][j]; if (mp[i][j] == 'S') { x[s] = i; y[s] = j; s++; } } } } inline void Prefix() { for (int k = 0;k < s;k++) { for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) rel[k][i - x[k] + 402][j - y[k] + 402] = mp[i][j]; } } for (int k = 0;k < s;k++) { for (int i = 1;i <= 810;i++) { for (int j = 1;j <= 810;j++) { if (rel[k][i][j] == 'X' || rel[k][i][j] == '#') saf[i][j] |= (1ll << k); if (rel[k][i][j] == 'X' || rel[k][i][j] == '.' || rel[k][i][j] == 'S') sig[i][j][0] |= (1ll << k); if (rel[k][i][j] == '#') sig[i][j][1] |= (1ll << k); if (rel[k][i][j] == 'o') sig[i][j][2] |= (1ll << k); } } } } inline int Work(long long sta) { //cout << "work " << sta << endl; memset(vis, 0, sizeof(vis)); memset(isg, 0, sizeof(isg)); queue <pair <int, int> > que; que.push(make_pair(402, 402)); vis[402][402] = 1; while (!que.empty()) { int x = que.front().first, y = que.front().second; que.pop(); for (int k = 0;k < 4;k++) { int tx = x + Next[k][0], ty = y + Next[k][1]; if (tx < 1 || tx > 810 || ty < 1 || ty > 810) continue; if (sta & saf[tx][ty]) continue; if (vis[tx][ty]) continue; vis[tx][ty] = 1; que.push(make_pair(tx, ty)); } } for (int i = 1;i <= 810;i++) { for (int j = 1;j <= 810;j++) { if (vis[i][j]) { for (int k = 0;k < 9;k++) { int tx = i + Sight[k][0], ty = j + Sight[k][1]; if (tx < 1 || tx > 810 || ty < 1 || ty > 810) continue; isg[tx][ty] = 1; } } } } for (int i = 1;i <= 810;i++) { for (int j = 1;j <= 810;j++) { if (isg[i][j]) { //cout << i << " " << j << endl; for (int c = 0;c < 3;c++) { if ((sig[i][j][c] & sta) != 0 && (sig[i][j][c] & sta) != sta) { return min(Work((sig[i][j][c] & sta)), Work(sta ^ (sig[i][j][c] & sta))); } } } } } int ans = 0; for (int i = 1;i <= 810;i++) { for (int j = 1;j <= 810;j++) { if (vis[i][j] && (sig[i][j][2] & sta) == sta) ans++; } } return ans; } int main() { Read(); Prefix(); //cout << "pass" << endl; cout << Work((1ll << s) - 1) << endl; return 0; }
- 1
信息
- ID
- 7450
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者