UVa 10231 Matrix World
题目描述
在现实世界中,有一个 M×NM \times NM×N 的网格地图(2≤N,M≤302 \leq N, M \leq 302≤N,M≤30)。地图中包含以下几种元素:
*:钻石宝藏.:空单元格#:岩石(不可通过)X:警卫机器人O:你的起始位置
你从一个单元格移动到相邻单元格需要 111 秒,收集一个宝藏也需要 111 秒。警卫机器人也以 111 格/秒的速度移动。你不能走出地图边界,警卫也无法进入地图(但警卫可以在地图内移动)。
你需要收集尽可能多的宝藏。如果收集到相同数量的宝藏有多种方案,选择总时间最小的方案。注意:如果你到达一个宝藏单元格的时刻为 ttt,那么你将在 t+1t+1t+1 秒完成收集。如果此时有警卫也到达该单元格(即警卫到达时间 ≤t+1\leq t+1≤t+1),则该宝藏不能计入收集结果,因为你没有时间逃脱。
输入包含多个测试用例,输出格式如下:
- 每个用例先输出
Case t: - 如果无法收集任何宝藏,输出
No treasures can be collected. - 否则输出:
Maximum number of collectible treasures: max.Minimum Time: min sec.
- 每个用例后输出一个空行。
题目分析
本题是一个 带有时间约束的路径规划 + 状态压缩动态规划 问题。地图规模最大为 30×3030 \times 3030×30,但宝藏数量不超过 101010,这提示我们可以对宝藏集合进行状态压缩。
核心难点
- 警卫的动态影响:警卫从各自的位置同时出发,其可达范围随时间扩张。我们需要知道每个单元格被警卫最早到达的时间,从而判断玩家能否安全经过或停留。
- 收集时间与逃脱时间:玩家在宝藏格必须额外停留 111 秒来完成收集,这比普通移动更危险,因为警卫可能在这 111 秒内赶到。
- 最优顺序:由于宝藏数量少,我们可以枚举收集顺序,但直接枚举 10!10!10! 不可行,因此采用状态压缩 DP\texttt{DP}DP。
关键观察
- 玩家的安全条件:若玩家在时刻 ttt 到达单元格 (i,j)(i, j)(i,j),则必须满足 t<guardTime[i][j]t < guardTime[i][j]t<guardTime[i][j]。对于收集操作,若在时刻 ttt 开始收集,则收集完成时刻为 t+1t+1t+1,必须满足 t+1<guardTime[i][j]t+1 < guardTime[i][j]t+1<guardTime[i][j]。
- 警卫的移动不受玩家影响,且所有警卫同时从初始位置出发。这允许我们预先用 BFS\texttt{BFS}BFS 计算出每个格子的警卫最早到达时间。
- 玩家只能走空单元格、宝藏格、起始格,不能走岩石格。
- 由于地图大小有限,任意两点之间的 安全最短路径 可以预处理(使用 BFS\texttt{BFS}BFS,排除不安全的时刻)。
解题思路
第一步:预处理警卫到达时间
设 guardTime[x][y]guardTime[x][y]guardTime[x][y] 表示任意警卫最早到达单元格 (x,y)(x, y)(x,y) 的时间。由于所有警卫同时出发,我们可以将所有警卫的初始位置加入队列,运行多源 BFS\texttt{BFS}BFS。对于岩石格 #,警卫也无法进入,因此 BFS\texttt{BFS}BFS 中跳过它们。
初始化:每个警卫位置的 guardTime=0guardTime = 0guardTime=0。
转移:从 (x,y)(x, y)(x,y) 走到相邻的 (nx,ny)(nx, ny)(nx,ny),若 guardTime[nx][ny]>guardTime[x][y]+1guardTime[nx][ny] > guardTime[x][y] + 1guardTime[nx][ny]>guardTime[x][y]+1,则更新并入队。
第二步:计算玩家安全移动的时间矩阵
设起点为 SSS,宝藏共有 KKK 个(K≤10K \leq 10K≤10)。我们构建一个 (K+1)×(K+1)(K+1) \times (K+1)(K+1)×(K+1) 的距离矩阵 distdistdist,其中索引 000 代表起点,索引 1…K1 \ldots K1…K 代表各个宝藏。
对于任意两个点 AAA 和 BBB(可以是起点或宝藏),我们使用 BFS\texttt{BFS}BFS 计算从 AAA 到 BBB 的 安全最短路径长度(即移动步数)。BFS\texttt{BFS}BFS 的限制条件:
- 只能走
'.'、'*'、'O'(起点位置)。 - 当从当前单元格到达邻居单元格时,设到达邻居的时刻为 t+1t+1t+1,必须满足 t+1<guardTime[neighbor]t+1 < guardTime[neighbor]t+1<guardTime[neighbor]。
注意:如果起点本身在时刻 000 就与警卫重合(即起点有警卫),则无法开始任何行动。在我们的多源 BFS\texttt{BFS}BFS 中,若 guardTime[S]=0guardTime[S] = 0guardTime[S]=0,则玩家无法存活。
第三步:状态压缩 DP\texttt{DP}DP
我们使用二进制掩码表示已经收集的宝藏集合。定义:
dp[mask][i]=收集完掩码 mask 中的宝藏,且最后收集的宝藏是 i 的最小总时间(已完成收集) dp[mask][i] = \text{收集完掩码 } mask \text{ 中的宝藏,且最后收集的宝藏是 } i \text{ 的最小总时间(已完成收集)} dp[mask][i]=收集完掩码 mask 中的宝藏,且最后收集的宝藏是 i 的最小总时间(已完成收集)
其中 iii 是宝藏的编号(0≤i<K0 \leq i < K0≤i<K)。注意 dp[mask][i]dp[mask][i]dp[mask][i] 已经包含了最后一次收集所需的 111 秒。
初始化:对于每个宝藏 iii,计算从起点 SSS 走到宝藏 iii 的安全时间 tmovet_{move}tmove。若 tmove<∞t_{move} < \inftytmove<∞ 且 tmove+1<guardTime[宝藏i]t_{move} + 1 < guardTime[宝藏_i]tmove+1<guardTime[宝藏i],则:
dp[1≪i][i]=tmove+1 dp[1 \ll i][i] = t_{move} + 1 dp[1≪i][i]=tmove+1
状态转移:对于当前状态 (mask,last)(mask, last)(mask,last),尝试前往下一个未收集的宝藏 nxtnxtnxt。设从 lastlastlast 到 nxtnxtnxt 的安全移动时间为 d=dist[last+1][nxt+1]d = dist[last+1][nxt+1]d=dist[last+1][nxt+1](注意索引偏移)。设到达 nxtnxtnxt 的时刻为 arrive=dp[mask][last]+darrive = dp[mask][last] + darrive=dp[mask][last]+d,完成收集的时刻为 collectEnd=arrive+1collectEnd = arrive + 1collectEnd=arrive+1。若 collectEnd<guardTime[宝藏nxt]collectEnd < guardTime[宝藏_{nxt}]collectEnd<guardTime[宝藏nxt],则更新:
dp[mask∣(1≪nxt)][nxt]=min(dp[mask∣(1≪nxt)][nxt], collectEnd) dp[mask \mid (1 \ll nxt)][nxt] = \min(dp[mask \mid (1 \ll nxt)][nxt],\ collectEnd) dp[mask∣(1≪nxt)][nxt]=min(dp[mask∣(1≪nxt)][nxt], collectEnd)
最终答案:遍历所有 maskmaskmask 和 lastlastlast,找出 popcount(mask)popcount(mask)popcount(mask) 最大的 dp[mask][last]dp[mask][last]dp[mask][last],若存在多个相同数量的宝藏,取时间最小的。
第四步:边界情况处理
- 如果起点就是宝藏?题目中没有说明,但按逻辑起点是
O,宝藏是*,二者不同。 - 如果地图中没有宝藏,直接输出无法收集。
- 如果所有宝藏都被警卫包围导致无法安全收集,也输出无法收集。
复杂度分析
- 多源 BFS\texttt{BFS}BFS:O(M×N)O(M \times N)O(M×N)
- 预处理每对起点/宝藏之间的安全距离:O((K+1)2×M×N)O((K+1)^2 \times M \times N)O((K+1)2×M×N),由于 K≤10K \leq 10K≤10,M,N≤30M, N \leq 30M,N≤30,最多约 112×900=10890011^2 \times 900 = 108900112×900=108900 次 BFS\texttt{BFS}BFS,每次 BFS\texttt{BFS}BFS 为 O(MN)O(MN)O(MN),实际可接受。
- DP 状态数:2K×K≈1024×10=102402^K \times K \approx 1024 \times 10 = 102402K×K≈1024×10=10240,转移 O(K)O(K)O(K),总 O(2K×K2)O(2^K \times K^2)O(2K×K2),非常快。
代码实现
// Matrix World
// UVa ID: 10231
// Verdict: Accepted
// Submission Date: 2026-05-29
// UVa Run Time: 0.010s
//
// 版权所有(C)2026,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
struct State {
int x, y, t;
};
int main() {
int m, n, caseNo = 1;
while (cin >> m >> n) {
vector<string> grid(m);
for (int i = 0; i < m; i++) cin >> grid[i];
vector<pair<int, int>> treasures, guards;
int sx = -1, sy = -1;
// 收集位置信息:'*' 宝藏,'X' 警卫,'O' 起点
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '*') treasures.push_back({i, j});
else if (grid[i][j] == 'X') guards.push_back({i, j});
else if (grid[i][j] == 'O') sx = i, sy = j;
}
}
int tCnt = treasures.size();
// 多源BFS计算警卫到达每个格子的最短时间
vector<vector<int>> guardTime(m, vector<int>(n, INF));
queue<State> q;
for (auto& g : guards) {
guardTime[g.first][g.second] = 0;
q.push({g.first, g.second, 0});
}
while (!q.empty()) {
State cur = q.front(); q.pop();
for (int d = 0; d < 4; d++) {
int nx = cur.x + dx[d], ny = cur.y + dy[d];
if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
if (grid[nx][ny] == '#') continue;
if (guardTime[nx][ny] > cur.t + 1) {
guardTime[nx][ny] = cur.t + 1;
q.push({nx, ny, cur.t + 1});
}
}
}
// BFS计算从(sx,sy)到(tx,ty)的安全最短路径时间(移动时间)
auto bfs = [&](int sx, int sy, int tx, int ty) -> int {
if (sx == tx && sy == ty) return 0;
vector<vector<int>> dist(m, vector<int>(n, INF));
queue<State> qq;
// 起点必须在警卫到达之前才能开始(严格小于)
if (guardTime[sx][sy] > 0) {
dist[sx][sy] = 0;
qq.push({sx, sy, 0});
}
while (!qq.empty()) {
State cur = qq.front(); qq.pop();
for (int d = 0; d < 4; d++) {
int nx = cur.x + dx[d], ny = cur.y + dy[d];
if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
if (grid[nx][ny] == '#') continue;
int nt = cur.t + 1;
if (nt >= guardTime[nx][ny]) continue;
if (dist[nx][ny] > nt) {
dist[nx][ny] = nt;
qq.push({nx, ny, nt});
}
}
}
return dist[tx][ty];
};
// 建立距离矩阵:索引0是起点,1..tCnt是宝藏
vector<vector<int>> dist(tCnt + 1, vector<int>(tCnt + 1, INF));
vector<pair<int, int>> points;
points.push_back({sx, sy});
for (auto& t : treasures) points.push_back(t);
for (int i = 0; i <= tCnt; i++) {
for (int j = i + 1; j <= tCnt; j++) {
int d1 = bfs(points[i].first, points[i].second, points[j].first, points[j].second);
int d2 = bfs(points[j].first, points[j].second, points[i].first, points[i].second);
if (d1 < INF) dist[i][j] = d1;
if (d2 < INF) dist[j][i] = d2;
}
}
// 状态压缩DP:dp[mask][last] = 收集完last后总时间(已包含收集的1秒)
vector<vector<int>> dp(1 << tCnt, vector<int>(tCnt, INF));
for (int i = 0; i < tCnt; i++) {
int d = bfs(sx, sy, points[i + 1].first, points[i + 1].second);
if (d < INF) {
int collectEnd = d + 1;
if (collectEnd < guardTime[points[i + 1].first][points[i + 1].second])
dp[1 << i][i] = collectEnd;
}
}
for (int mask = 1; mask < (1 << tCnt); mask++) {
for (int last = 0; last < tCnt; last++) {
if (!(mask >> last & 1)) continue;
if (dp[mask][last] == INF) continue;
for (int nxt = 0; nxt < tCnt; nxt++) {
if (mask >> nxt & 1) continue;
int d = dist[last + 1][nxt + 1];
if (d >= INF) continue;
int arrive = dp[mask][last] + d;
int collectEnd = arrive + 1;
if (collectEnd < guardTime[points[nxt + 1].first][points[nxt + 1].second])
dp[mask | (1 << nxt)][nxt] = min(dp[mask | (1 << nxt)][nxt], collectEnd);
}
}
}
// 找出最大可收集宝藏数及最小时间
int maxTreasures = 0, minTime = INF;
for (int mask = 1; mask < (1 << tCnt); mask++) {
int cnt = __builtin_popcount(mask);
if (cnt < maxTreasures) continue;
for (int last = 0; last < tCnt; last++) {
if (dp[mask][last] < INF) {
if (cnt > maxTreasures) {
maxTreasures = cnt;
minTime = dp[mask][last];
} else if (cnt == maxTreasures) {
minTime = min(minTime, dp[mask][last]);
}
}
}
}
cout << "Case " << caseNo++ << ":\n";
if (maxTreasures == 0) {
cout << "No treasures can be collected.\n";
} else {
cout << "Maximum number of collectible treasures: " << maxTreasures << ".\n";
cout << "Minimum Time: " << minTime << " sec.\n";
}
cout << "\n";
}
return 0;
}
总结
本题的核心在于 将警卫的威胁转化为时间约束,然后通过预处理安全路径和状态压缩 DP\texttt{DP}DP 来求解最优收集顺序。这种“时间约束 + 状态压缩”的模型在类似题目(如“收集物品并躲避敌人”)中非常常见,值得熟练掌握。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)