29.安排超市(卡码网第一期模拟笔试)
题目描述
给定一个 n*n 的地图。地图是上下左右四联通的,但是不能斜向行走。
其中:
*代表障碍,不可通行;
.代表路,可以通行;
#代表房子。房子也是可以通行的。
现在需要在一些地方安排一些超市(不能安排在障碍物上,可以安排在路上或者房子上,超市也是可以通行的),每个房子至少可以到达一个超市。同时由于成本原因,超市的数量需要尽可能的少。
在超市数量最少的情况下,每个房子到达最近的超市的距离之和需要尽可能小。
你的任务是计算超市最少的数量,以及最小的距离之和。
输入
第一行包含一个正整数 n,代表地图的大小(1 <= n <= 50)。 接下来的 n 行,每行包含一个长度为 n 的字符串,表示整个地图。
输出
输出两个整数,用空格隔开。分别代表超市的最小数量、最小的距离之和。
样例输入
3
#.#
.**
*.#
样例输出
2 2
提示
下标从 0 开始,第一个超市安排的位置是(0,1),第二个超市安排的位置是(2,2)。 三个房子到超市的距离分别为1、1、0。这样可以使得三个房子都能到达超市,并且所需超市数量最少,总距离之和也最少。
注意:超市直接建在房子上距离为 0。如果没有房子,则不需要建超市。
题解1(C++版本)
#include<bits/stdc++.h>
using namespace std;
const int N = 53, INF = 1e9;
int n, min_market, min_dis;
char mp[N][N];
int dx[] = {1,0,-1,0};
int dy[] = {0,1,0,-1};
bool vis[N][N], v[N][N];
bool check(int x, int y){
if(x < 1 || x > n || y < 1 || y > n || mp[x][y] == '*') return false;
return true;
}
vector<pair<int, int>> points; //保存该连通分量中的所有可以不在障碍物上的点
void dfs(int x, int y){
vis[x][y] = 1;
points.push_back({x, y}); //将同一个连通分量中的点加入到一个集合中
for(int i = 0; i < 4; i++) {
int tx = x + dx[i];
int ty = y + dy[i];
if(!check(tx, ty) || vis[tx][ty]) continue;
dfs(tx, ty);
}
}
struct node{
int x, y, step;
}tmp;
int bfs(int x, int y){ //获取以(x,y)为超市到该区域内所有房子的最小距离
queue<node> q;
memset(v, 0, sizeof v);
q.push({x, y, 0});
v[x][y] = 1;
int dis = 0;
while(!q.empty()) {
tmp = q.front(); q.pop();
if(mp[tmp.x][tmp.y] == '#') dis += tmp.step;
for(int i = 0; i < 4; i++){
int tx = tmp.x + dx[i];
int ty = tmp.y + dy[i];
if(!check(tx, ty) || v[tx][ty]) continue;
q.push({tx, ty, tmp.step + 1});
v[tx][ty] = 1;
}
}
return dis;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%s", mp[i] + 1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
points.clear(); //清空上一个区域内的所有房子
if(mp[i][j] == '#' && !vis[i][j]){
min_market++; //连通分量数加1
dfs(i, j);
int cur_dis = INF;
for(auto point : points){ //遍历该连通分量所有的点,找到一点到所有房子的最小距离
cur_dis = min(cur_dis, bfs(point.first, point.second));
}
min_dis += cur_dis;
}
}
}
printf("%d %d\n", min_market, min_dis);
return 0;
}