AOJ-0558 Chess(BFS最短路)¶
原文是日文,这里翻译成了中文,评测在virtual judge
芝士¶
问题问题¶
同样,今年,JOI镇的一家奶酪工厂也开始生产奶酪,一只老鼠从窝里出来。JOI镇分为北,南,东和西,每个地块都是巢,奶酪工厂,障碍物或空地。鼠标从巢穴开始,并访问所有奶酪工厂,一次只能吃一种奶酪。
该镇有N个奶酪工厂,每个工厂仅生产一种奶酪。奶酪的硬度因工厂而异,并且恰好有一个奶酪工厂生产硬度为1到N的奶酪。
老鼠的初始健康状况是1,每吃一块奶酪,您的健康状况就会增加1。但是,老鼠不能吃比其体力更坚硬的奶酪。
鼠标可以在一分钟内移动到相邻的东西南北向隔间,但不能进入障碍物隔间。您甚至可以不吃奶酪就通过奶酪工厂。编写一个程序,以最短的时间完成所有奶酪的食用。但是,小鼠吃奶酪所需的时间可以忽略不计。
输入项¶
输入为H + 1行。在第一行中,三个整数H,W和N(1≤H≤1000、1≤W≤1000、1≤N≤9)以空格隔开。从第2行到第H + 1行的每一行都包含一个W字符串,由'S','1','2',...,'9','X'和'。组成。它被编写,每个代表每个部分的状态。如果将北起的第i个分段和西起的第j个分段描述为(i,j)(1≤i≤H,1≤j≤W),则第i + 1行中的第j个字符为如果块(i,j)是一个嵌套,它将是'S',如果是一个障碍,它将是'X',如果是一个空地,它将是'。',并且其硬度将为1,2,...,9如果是一家生产奶酪的工厂,则分别为“ 1”,“ 2”,...,“ 9”。输入有一个巢穴和一个工厂,生产的奶酪的硬度为1,2,...,N。其他牢房一定会成为障碍物或空缺。保证老鼠可以吃所有的奶酪。
输出量¶
在一行上打印一个整数,代表完成吃完所有奶酪所需的最短时间(以分钟为单位)。
输入/输出示例¶
输入示例1¶
3 3 1
S ..
...
..1
输出示例1¶
4
输入示例2¶
4 5 2
.X..1
.... X
.XX.S
.2.X。
输出示例2¶
12
输入例3¶
10 10 9
.X ... XSX
6..5X..X1X
... XXXX..X
X..9X ... X.
8.X2X..X3X
XX.X4 ..
XX .... 7X ..
X..X..XX ..
X ... X.XX ..
..X .......
输出示例3¶
91
¶
91
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 0x0ffffff;
struct Node {
int row, col;
Node(int x, int y) : row(x), col(y) {}
};
int m = 1000, n = 1000;
int num = 9;
vector<Node> factory(num + 1);
vector<vector<char> > ground(m, vector<char>(n));
vector<vector<int> > pathLen(m, vector<int>(n, INF));
int direction[4][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}};
inline void init()
{
for (int i = 0; i < m; ++i) {
fill(pathLen[i].begin(), pathLen[i].begin() + n, INF);
}
}
inline bool canGo(int x, int y)
{
return (0 <= x && x < m && 0 <= y && y < n
&& ground[x][y] != 'X' && pathLen[x][y] == INF);
}
int BFS(Node start, Node end)
{
init();
queue<Node> q;
q.push(start);
pathLen[start.row][start.col] = 0;
while (!q.empty()) {
Node e = q.front(); q.pop();
for (int i = 0; i < 4; ++i) {
int nextRow = e.row + direction[i][0];
int nextCol = e.col + direction[i][1];
if (canGo(nextRow, nextCol)) {
pathLen[nextRow][nextCol] = pathLen[e.row][e.col] + 1;
if (nextRow == end.row && nextCol == end.col) {
return pathLen[nextRow][nextCol];
}
q.push(Node(nextRow, nextCol));
}
}
}
}
int main()
{
std::ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> m >> n >> num;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> ground[i][j];
if (ground[i][j] == 'S') {
factory[0].row = i;
factory[0].col = j;
}
else if ('1' <= ground[i][j] && ground[i][j] <= '9') {
int number = ground[i][j] - '0';
factory[number].row = i;
factory[number].col = j;
}
}
}
int sum = 0;
for (int i = 1; i <= num; ++i) {
sum += BFS(factory[i-1], factory[i]);
}
cout << sum << endl;
return 0;
}
这道题目本质上和《挑战程序设计竞赛》的走迷宫例题是一样的,但是给了一点迷惑的信息。最初写的很复杂,考虑的是需要一个health
变量,然后搜索距离当前节点的所有工厂中,可以被访问的(健康值满足)的最短距离,然后用了两个queuq
来恢复走过的路径(精确版的init()
),但是这样就属于暴力模拟,实际上仔细分析题意,初始健康值是1,只能吃标号为1的工厂,然后健康值为2,小于等于2的只剩下2号工厂,所以转化成从1到2,2到3 ,……,8到9的最短路径,转化成了图中给定两点求最短路的问题。有一个细节,就是我们用数组pathLen()
来记录当前节点是否被访问过,以及从起始点到当前点所走的最短路长度,但是下一轮的时候要记得把走过的路径恢复,为了简便就直接全部初始化。