给定一个n*n的地图。地图是上下左右四联通的,不能斜向行走:
*代表障碍,不可通行。
.代表路,可以通行。
#代表房子。房子也是可以通行的。
小红现在需要在一些地方安排一些超市(不能安排在障碍物上,可以安排在路上或者房子上。超市也是可以通行的)。
小红希望每个房子至少可以到达一个超市。同时由于成本原因,小红希望超市的数量尽可能少。
在超市数量最少的情况下,小红希望每个房子到达最近的超市的距离之和尽可能小。
她想知道超市最少的数量,以及最小的距离之和。你能帮帮她吗?
第一行一个正整数n,代表地图的大小。( 1<=n<=50 ) 接下来的n行,每行一个长度为n的字符串,表示整个地图。保证输入合法。
输出两个整数,用空格隔开。分别代表超市的最小数量、最小的距离之和。
3 #.# .** *.#
2 2
下标从1开始,第一个超市安排的位置是(1,2),第二个超市安排的位置是(3,3)。三个房子到超市的距离分别为1,1,0。
3 #*# .** *.#
3 0
分别在三个房子上建3个超市即可。
2 .* *.
0 0
没有房子,所以不用造超市
首先需要计算商店最少个数,每一个房子都可以前往商店购物,即'*'将地图分成了n份,那么就是n个商店(深度优先算法)
然后计算每个区域内的房子到达商店的距离和最小,也就保证了总体最小(广度优先算法)。
import java.util.*;
/**
* @author xuan
* @date 2022/4/15 12:47
*/
public class No4 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
char[][] map = new char[n][n];
char[][] map1 = new char[n][n];
int[][] H = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int ans1 = 0;
int ans2 = 0;
for (int i = 0; i < n; i++) {
map[i] = sc.nextLine().trim().toCharArray();
map1[i] = map[i].clone();
}
//对每个节点进行深度优先递归统计区域个数,并且统计区域内包含的节点
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
//节点为房子需要递归。可能一个区域内没有'#',那么不需要递归
if (map1[i][j] == '#') {
ans1++; //区域个数也就是商店数量
List list = new ArrayList();//统计本区域内的所有节点
dfs(map1, i, j, list);//深度优先递归将这个区域内的节点全部遍历
int min = Integer.MAX_VALUE;
//将商店设置在coordinate,统计此区域内最小距离和
for (int[] coordinate : list) {
int min1 = 0;
int step = 0; //商店到节点的距离
//广度优先
boolean[][] flag = new boolean[n][n];
Queue queue = new LinkedList();
queue.offer(coordinate);
flag[coordinate[0]][coordinate[1]] = true;
while (!queue.isEmpty()) {
int size = queue.size();
for (int k = 0; k < size; k++) {
int[] poll = queue.poll();
int x = poll[0], y = poll[1];
//如果此节点是房子,那么需要统计距离
if (map[x][y] == '#') {
min1 += step;
}
for (int l = 0; l < 4; l++) {
int xx = x + H[l][0];
int yy = y + H[l][1];
//超过边界或者不是此区域或者遍历过 则不加入队列
if (xx = n || yy >= n || map[xx][yy] == '*' || flag[xx][yy]) {
continue;
}
queue.offer(new int[]{xx, yy});
//标记已遍历
flag[xx][yy] = true;
}
}
step++;
}
//求出此区域内的最小距离和
min = Math.min(min1, min);
}
//总的距离和
ans2 += min;
}
}
}
System.out.println(ans1 + " " + ans2);
}
/**
*
* @param map 备份的地图
* @param x 纵坐标
* @param y 横坐标
* @param list 统计此区域内的节点
*/
public static void dfs(char[][] map, int x, int y, List list) {
int n = map.length;
if (x = n || y >= n || map[x][y] == '*') {
return;
}
list.add(new int[]{x, y});
//标记已遍历
map[x][y] = '*';
//遍历上下左右
dfs(map, x - 1, y, list);
dfs(map, x + 1, y, list);
dfs(map, x, y - 1, list);
dfs(map, x, y + 1, list);
}
}