//664K 79MS G++
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define LENGTH_MAX 54
#define NODE_MAX 110
#define WALL -1
int maze[LENGTH_MAX][LENGTH_MAX]; // 0: nothing -1: wall >0: S or A's id
int node_distance[NODE_MAX][NODE_MAX];
struct Node {
int x;
int y;
};
typedef struct Node Node;
Node nodes[NODE_MAX];
int nodeNum;
int mazeLength;
int mazeWidth;
struct BFSNode {
int x;
int y;
int distance;
};
typedef struct BFSNode BFSNode;
queue<BFSNode> BFSQueue;
int BFSFlag[NODE_MAX][NODE_MAX];
void processNode(int curX, int curY, int curDistance, int beginId) {
BFSFlag[curX][curY] = 1;
int upNodeId = maze[curX][curY];
if (upNodeId > 0) { // Node in this (x,y)
node_distance[beginId][upNodeId] = curDistance + 1; // get its shortest distance
} else if (upNodeId == WALL) {
return;
}
BFSNode upNode;
upNode.x = curX;
upNode.y = curY;
upNode.distance = curDistance + 1;
BFSQueue.push(upNode);
}
void BFS(int beginId) {
while(BFSQueue.size()) {
BFSQueue.pop();
}
memset(BFSFlag, 0, sizeof(BFSFlag));
int beginX = nodes[beginId].x;
int beginY = nodes[beginId].y;
BFSNode beginNode;
beginNode.x = beginX;
beginNode.y = beginY;
beginNode.distance = 0;
BFSFlag[beginX][beginY] = 1;
BFSQueue.push(beginNode);
while(BFSQueue.size()) {
BFSNode curNode = BFSQueue.front();
BFSQueue.pop();
int curX = curNode.x;
int curY = curNode.y;
int distance = curNode.distance;
//up
if ((curY-1>=0) && !BFSFlag[curX][curY-1]) { //if can up
processNode(curX, curY-1, distance, beginId);
}
//Down
if ((curY + 1 <= mazeWidth-1) && !BFSFlag[curX][curY+1]) {
processNode(curX, curY+1, distance, beginId);
}
//left
if ((curX-1 >= 0) && !BFSFlag[curX-1][curY]) {
processNode(curX-1, curY, distance, beginId);
}
//right
if ((curX + 1 <= mazeLength-1) && !BFSFlag[curX+1][curY]) {
processNode(curX+1, curY, distance, beginId);
}
BFSFlag[curX][curY] = 2;
}
}
void getNodeDistance() {
for (int i = 1; i <= nodeNum; i++) {
BFS(i);
}
// for (int i = 1; i <= nodeNum; i++) {
// for (int j = 1; j <= nodeNum; j++) {
// printf("%d -> %d: %d\n", i,j, node_distance[i][j]);
// }
// }
}
int key[NODE_MAX];
char visited[NODE_MAX];
#define INF 99999999
void Prim() {
int distanceSum = 0;
for (int i = 1 ; i <= nodeNum; i++) {
key[i] = INF;
visited[i] = 0;
}
key[1] = 0;
for (int i = 1; i <= nodeNum; i++) {
int minId = -1;
int minDistance = INF;
for (int j = 1; j <= nodeNum; j++) {
if (!visited[j] && minDistance > key[j]) { // get the shortest unprocessed node.
minId = j;
minDistance = key[j];
}
}
visited[minId] = 1;
// printf("%d %d\n", minId, minDistance);
distanceSum += minDistance;
for (int j = 1; j <= nodeNum; j++) {
if (!visited[j] && node_distance[minId][j] > 0) { // check minId's adjancey
if (key[j] > node_distance[minId][j]) {
key[j] = node_distance[minId][j];
}
}
}
}
printf("%d\n", distanceSum);
}
void solve() {
getNodeDistance();
Prim();
}
int main() {
int caseNum;
char tmp[LENGTH_MAX] = "";
// scanf("%d", &caseNum);
gets(tmp);
sscanf(tmp , "%d", &caseNum);
for (int i = 0; i < caseNum; i++) {
nodeNum = 0;
memset(maze, 0, sizeof(maze));
memset(node_distance, 0, sizeof(node_distance));
memset(nodes, 0, sizeof(nodes));
// scanf("%d %d", &mazeLength, &mazeWidth);
char mazeline[LENGTH_MAX] = "";
gets(mazeline);
// printf("! %s", mazeline);
sscanf(mazeline , "%d %d", &mazeLength, &mazeWidth);
// printf("%d %d\n", mazeLength, mazeWidth);
for (int i = 0; i < mazeWidth; i++) {
memset(mazeline, 0, sizeof(mazeline));
gets(mazeline);
// printf("%s\n", mazeline);
for (int j = 0; j < mazeLength; j++) {
if (mazeline[j] == '#') {
maze[i][j] = WALL;
} else if (mazeline[j] == 'S' || mazeline[j] == 'A') {
// printf("nodeNum %d %c\n", nodeNum, mazeline[j]);
nodeNum++;
maze[i][j] = nodeNum;
nodes[nodeNum].x = i;
nodes[nodeNum].y = j;
}
}
}
// printf("nodeNum %d\n", nodeNum);
solve();
}
}
算是一道好的综合题,转化和综合难度都适中.
初看题可能不是很好理解题意,不过有个关键的地方就是在最开始搜索迷宫 或者 遇到alien的时候,队伍可以分裂为数个进行搜索,
这其实就暗地说明了从任何一个 'A'/'S' 可以到达任何其他的'A'/'S'(从S搜索开始, 可以分裂为数个队伍到其他的任何数量的'A', 而在‘A’这里,队伍同样可以分裂数个到其他任何
的'A'/'S', 题目还专门说明了搜索队伍大于100,保证了就算再分裂所有的alien(最多100)也可以被到达)
这样就可以把 'A'/'S'看做图的V, 并且每个V都是互相直接可达的(题目说明了),
而结合题目要求的所有队列最后经过的路径的总长度的最小值,其实就是能遍历每个V的最短路径了,即最小生成树。
到了这里,就到第一个考点了,题目没有直接给出每个'A'/'S'之间的距离,而是给了一个字符表示的迷宫分布图。
每个V之间的(最短)距离都要用这张图求出来,这个问题就转化成了迷宫求最短路径了(每两个V之间的最短距离)。
而求迷宫最短路径都是用BFS,这里如果按照朴素思维,就要一一求两个点之间的最短距离,需要100*99次, 这样铁定TLE.
不过因为用的是BFS,因此可以把求两个点的最短路径问题变为,从某个源点开始遍历迷宫所有位置,求出与其他点最短路径的问题(因为遍历过程中必然会经过其他所有的点,并且一定是最短路径),这样对于每个点只需要BFS一次就可以了,最多只需要100次,满足了时间需求。(其实上面的次数计算不严格,因为在本题中,边是无向的,求出了A->B的最短距离 ,B->A的最短距离也就得到了,不过即使这样,还是后者效率高)。
得到了边的权值以后,直接Prim算法求出最小生成树的长度即可.