题目链接在这里
题目大意:
一群人从起点S开始找外星人,#代表墙壁,A代表外星人。问把所有的外星人找到最少需要多少步。
思路分析:
对每一个为A或者S的点进行bfs,算出来和其它点的最短距离。然后把这些两两之间的距离当成边,构造最小生成树。
注意!!!
1.题目先输入了列数再输入了行数。
2.将列数行数输入完之后要gets一下空格!我用的getchar()没用,wa掉了。
3.数组开大一点!我开的55 * 55没用。。。改成105 * 105才过的。
这次参考了金海峰的代码风格,感觉看起来挺简约的。
代码中有kruskal和prim两种算法,想试另外一种算法的,就直接将输出的prim()改成kruskal()就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MaxN = 105;
bool map[MaxN][MaxN];
int dis[MaxN][MaxN];
int cost[MaxN][MaxN];
char str[MaxN][MaxN];
int t, n, m, cnt;
int mx[4] = {0, 0, -1, 1};
int my[4] = {-1, 1, 0, 0};
struct Point{
int x, y;
}p[MaxN * MaxN];
struct Edge{
int x, y, val;
bool operator < (const Edge &e) const{
return val > e.val;
}
};
void input(){
scanf("%d %d", &m, &n);
gets(str[0]);
cnt = 1;
for(int i = 0; i < n; ++i){
gets(str[i]);
for(int j = 0; j < m; ++j){
if(str[i][j] == '#')
map[i][j] = false;
else{
map[i][j] = true;
if(str[i][j] == 'A')
p[cnt++] = Point{i, j};
if(str[i][j] == 'S')
p[0] = Point{i, j};
}
}
}
}
bool judge(int x, int y){
return x >= 0 && x < n && y >= 0 && y < m && map[x][y] && dis[x][y] == -1;
}
void bfs(const Point &p){
memset(dis, -1, sizeof(dis));
queue<Point> que;
que.push(p);
dis[p.x][p.y] = 0;
while(!que.empty()){
Point p1 = que.front();
que.pop();
for(int i = 0; i < 4; ++i){
int nx = p1.x + mx[i];
int ny = p1.y + my[i];
if(judge(nx, ny)){
//printf("nx:%d, ny:%d\n", nx, ny);
que.push(Point{nx, ny});
dis[nx][ny] = dis[p1.x][p1.y] + 1;
}
}
}
}
void make(){
for(int i = 0; i < cnt; ++i){
bfs(p[i]);
for(int j = 0; j < cnt; ++j){
cost[i][j] = dis[p[j].x][p[j].y];
//printf("i:%d, j:%d, cost:%d\n", i, j, cost[i][j]);
}
}
}
int prim(){
bool vis[MaxN * MaxN];
int lowc[MaxN * MaxN];
int ans = 0;
memset(vis, false, sizeof(vis));
vis[0] = true;
for(int i = 1; i < cnt; ++i)
lowc[i] = cost[0][i];
for(int i = 0; i < cnt - 1; ++i){
int Minc = INF;
int p = -1;
for(int j = 0; j < cnt; ++j){
if(Minc > lowc[j] && !vis[j]){
Minc = lowc[j];
p = j;
}
}
if(Minc == INF) return -1;
//printf("p:%d, cost:%d\n", p, Minc);
vis[p] = true;
ans += Minc;
for(int j = 0; j < cnt; ++j)
if(!vis[j] && lowc[j] > cost[p][j])
lowc[j] = cost[p][j];
}
return ans;
}
int par[MaxN * MaxN], r[MaxN * MaxN];
int Find(int x){
if(x == par[x]) return x;
return par[x] = Find(par[x]);
}
void unite(int x, int y){
x = Find(x);
y = Find(y);
if(x == y) return;
if(r[x] < r[y]) par[x] = y;
else{
par[y] = x;
if(r[x] == r[y]) ++r[x];
}
}
bool check(int x, int y){
return Find(x) == Find(y);
}
int kruskal(){
for(int i = 0; i <= cnt; ++i){
par[i] = i;
r[i] = 0;
}
priority_queue<Edge> que;
for(int i = 0; i < cnt; ++i){
for(int j = 0; j < i; ++j){
que.push(Edge{i, j, cost[i][j]});
}
}
int ans = 0;
while(!que.empty()){
Edge e = que.top();
que.pop();
if(check(e.x, e.y)) continue;
unite(e.x, e.y);
ans += e.val;
}
int cc = 0;
for(int i = 0; i < cnt; ++i)
if(par[i] == i) ++cc;
if(cc != 1) return -1;
return ans;
}
int main(){
scanf("%d", &t);
while(t--){
input();
make();
printf("%d\n", prim());
}
return 0;
}