题目链接
在一个 y行 x列 的迷宫中,有可行走的通路空格 ,不可行走的墙 #,还有两种英文字母 A 和 S ,现在从 S 出发,要求用最短的路径 L 连接所有字母,输出这条路径 L 的总长度。
BFS + Prim
一格的长度为1,而且移动的方法只有上、下、左、右,
所以在无任何墙的情况下(但“墙#”是必须考虑的,这里只是为了说明)
任意两个字母之间的距离就是直接把 横坐标之差 加上 纵坐标之差
注意的是: ① 可行的路为 字母 和 空格 ② 不可行的路为 # 和 矩阵范围之外
根据题意的**“分离”规则**,重复走过的路不再计算
因此当使用prim算法求L的长度时,根据算法的特征恰好不用考虑这个问题(源点合并很好地解决了这个问题),L就是最少生成树的总权值W
由于使用prim算法求在最小生成树,因此无论哪个点做起点都是一样的,(通常选取第一个点),因此起点不是S也没有关系
所以所有的A和S都可以一视同仁,看成一模一样的顶点就可以了
最后要注意的就是 字符的输入:
cin不读入空字符(包括 空格,换行等) gets读入空格,但不读入换行符)
剩下的问题关键就是处理 任意两字母间的最短距离,由于存在了“墙#” ,这个距离不可能单纯地利用坐标加减去计算,必须额外考虑,推荐用BFS(广搜、宽搜),这是本题的唯一难点,因为prim根本直接套用就可以了。
另外注意在求 任意两字母间的最短距离 时 不能直接 用 BFS:
(1) 必须先把矩阵中每一个允许通行的格看做一个节点(就是在矩阵内所有非#的格都作为图M的一个顶点),对每一个节点i,分别用BFS求出它到其他所有节点的权值(包括其本身,为0),构造节点图M; (2) 然后再加一个判断条件,从图M中抽取以字母为顶点的图,进而构造字母图N。这个判定条件就是当节点图M中的某点j为字母时,把i到j的权值再复制(不是抽离)出来,记录到字母图N的邻接矩阵中 (3) 剩下的就是对字母图N求最小生成树了
//252K 47MS #define inf 0x3f3f3f3f #define ll long long #define vec vector<int> #define P pair<int,int> #define MAX 55 int T, m, n, cnt, ma[MAX][MAX], dis[105][105], vis[MAX][MAX]; int dx[4] = { 1,-1,0,0 }, dy[4] = { 0,0,1,-1 }, mind[105], belong[105]; string s[MAX]; void bfs(int x, int y,int id) { fill(dis[id], dis[id] + 105, inf); memset(vis, -1, sizeof(vis)); dis[id][id] = 0; queue<P> q; q.push(P(x, y)); vis[x][y] = 0; while (!q.empty()) { x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (xx < 0 || xx >= m || yy < 0 || yy >= n || s[xx][yy] == '#' || vis[xx][yy] != -1)continue; vis[xx][yy] = vis[x][y] + 1; q.push(P(xx, yy)); if (ma[xx][yy] > 0) //这是一个点 dis[id][ma[xx][yy]] = vis[xx][yy];//更新到他的距离 } } } //从s开始的最小生成树 int prim(int s) { memset(belong, 0, sizeof(belong)); belong[s] = 1; for (int i = 1; i <= cnt; i++) mind[i] = dis[s][i]; int res = 0; //依次找到加入的cnt-1个点 for (int i = 1; i < cnt; i++) { int mi = inf, id = -1; //找到离当前点集最近的点 for (int j = 1; j <= cnt; j++) if (mind[j] < mi && !belong[j]) mi = mind[j], id = j; if (id == -1)break; belong[id] = 1; res += mind[id]; for (int j = 1; j <= cnt; j++) if (!belong[j] && mind[j] > dis[id][j]) mind[j] = dis[id][j]; } return res; } int main() { cin >> T; while (T--) { cin >> n >> m; char temp[51]; gets_s(temp); for (int i = 0; i < m; i++)getline(cin, s[i]); cnt = 1; //给每个A一个编号 for (int i = 0; i < m; i++) for (int j = 0; j < s[i].size(); j++) { if (s[i][j] == 'A')ma[i][j] = ++cnt; else if (s[i][j] == 'S')ma[i][j] = 1; else ma[i][j] = 0; } //计算每个A到其他节点的距离 for (int i = 0; i < m; i++) for (int j = 0; j < s[i].size(); j++) { if (ma[i][j] == 0)continue; bfs(i, j, ma[i][j]); } cout << prim(1) << endl; } }