给定一个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);
}
}
#include<bits/stdc++.h>
using namespace std;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
struct Node{
int x;
int y;
int dis;
};
//计算x,y到各个房子的距离之和
int dis(int x, int y, int N, vector<string> maps,
vector<vector<int>> visited){
queue<Node> q;
q.push(Node{x,y,0});
visited[x][y] = 1;
int ret = 0;
while(!q.empty()){
int size = q.size();
while (size--){
Node cur = q.front();
q.pop();
if (maps[cur.x][cur.y] == '#') ret += cur.dis;
for (int i = 0; i < 4; i++){
int xx = cur.x + dx[i];
int yy = cur.y + dy[i];
int ddis = cur.dis + 1;
if (xx >=0 && xx < N && yy >= 0 && yy < N && !visited[xx][yy]){
visited[xx][yy] = 1;
q.push(Node{xx,yy,ddis});
}
}
}
}
return ret;
}
//搜索当前区域
int findArea(int x, int y, int N, vector<vector<int>> &visited,
vector<pair<int,int>> &curArea,
vector<string> maps){
if (x < 0 || x >= N || y < 0 || y >= N || visited[x][y]) return 0;
visited[x][y] = 1;
int ret = 0;
//记录当前区域有多少房子
if (maps[x][y] == '#') ret++;
//记录当前区域所有点,为之后计算该区域所有位置到所有房子的距离做准备
curArea.emplace_back(make_pair(x,y));
//到处走走
for(int i = 0; i < 4; i++){
ret += findArea(x+dx[i], y+dy[i], N, visited,
curArea, maps);
}
return ret;
}
//计算当前区域在哪个位置建超市的总距离最短
int minDis(int N, vector<string> maps,
vector<pair<int,int>> area,
vector<vector<int>> visited){
int ret = int(1e9);
//该区域每个点都要试试
for(auto& pos: area){
ret = min(ret, dis(pos.first, pos.second, N, maps, visited));
}
return ret;
}
int main(void){
int N;
cin >> N;
vector<string> maps;
vector<vector<int>> visited(N,vector<int>(N,0));
//每个区域的所有点
vector<vector<pair<int,int>>> area;
//当前区域的所有点
vector<pair<int,int>> curArea;
//每个区域房子的数量
vector<int> areaHouseNum;
//当前区域房子的数量
int curAreaHouseNum = 0;
string temp;
bool house = false;
for (int i = 0; i < N; i++){
cin >> temp;
maps.emplace_back(temp);
for(int j = 0; j < N; j++){
if (temp[j] == '*') {
visited[i][j] = 1;
}
else if(temp[j] == '#') house = true;
}
}
//要没房子就别盖超市了
if (!house){
cout << 0 << ' ' << 0 << endl;
return 0;
}
vector<vector<int>> copy(visited);
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++){
if(!copy[i][j]){
curArea.clear();
curAreaHouseNum = findArea(i, j, N, copy, curArea, maps);
if (curAreaHouseNum){
area.emplace_back(curArea);
areaHouseNum.emplace_back(curAreaHouseNum);
}
}
}
}
int ans = 0;
//对每个区域进行距离的统计
for (int i = 0; i < area.size(); i++){
//如果当前区域房子只有一间,那么超市肯定建在房子的地方
if (areaHouseNum[i] == 1) continue;
//否则就要计算当前区域在哪建超市的总距离最短
ans += minDis(N, maps, area[i], visited);
}
cout << area.size() << ' ' << ans;
} n = int(input())
matrix = []
for _ in range(n):
line = (' '.join(input())).split()
matrix.append(line)
def spread(x, y,):
if x<0&nbs***bsp;x>=n&nbs***bsp;y<0&nbs***bsp;y>=n:
return
if matrix[x][y] == '.':
matrix[x][y] = '0' #路
poss.append([x,y])
elif matrix[x][y] == '#':
matrix[x][y] = '1' #房
houses.append([x,y])
poss.append([x,y])
else:
return
spread(x+1,y)
spread(x,y+1)
spread(x-1,y)
spread(x,y-1)
def caldis(x,y):
totaldis = 0
cur = [[x,y]]
rec = [[False]*n for _ in range(n)]
depth = 0
while cur:
temp = []
for pos in cur:
if pos[0] < 0&nbs***bsp;pos[0] >= n&nbs***bsp;pos[1] < 0&nbs***bsp;pos[1] >= n:
continue
if rec[pos[0]][pos[1]]&nbs***bsp;matrix[pos[0]][pos[1]] == '*': #障碍或者遍历过了
continue
if not rec[pos[0]][pos[1]]: # 没遍历过
if matrix[pos[0]][pos[1]] == '1': #且是房子
totaldis += depth
'''访问并扩散'''
rec[pos[0]][pos[1]] = True
temp.append([pos[0] + 1,pos[1]])
temp.append([pos[0] - 1, pos[1]])
temp.append([pos[0], pos[1] + 1])
temp.append([pos[0], pos[1] - 1])
cur = temp
depth += 1
return totaldis
def best_dis():
shortest_dis = float('inf')
if len(houses) == 1:
return 0
for pos in poss:
shortest_dis = min(shortest_dis, caldis(pos[0], pos[1]))
return shortest_dis
count = 0
dis = 0
for i in range(n):
for j in range(n):
if matrix[i][j] == '#':
poss = []
houses = []
count += 1
spread(i,j)
dis += best_dis()
else:
print(count, dis) #include "bits/stdc++.h"
using namespace std;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n; cin >> n;
vector<string> s(n);
for(int i = 0;i < n; i++) cin >> s[i];
static vector<int> dx{1, 0, -1, 0};
static vector<int> dy{0, 1, 0, -1};
vector<vector<bool>> vis(n, vector<bool>(n));
vector<pair<int, int>> p;
bool flag = 0;
auto bfs1 = [&](int x, int y) {
queue<pair<int, int>> q;
q.push(make_pair(x, y));
while(!q.empty()) {
int x, y;
tie(x, y) = q.front();
q.pop();
if(s[x][y] == '#') flag = 1;
p.push_back(make_pair(x, y));
for(int i = 0;i < 4; i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 0 || xx >= n || yy < 0 || yy >= n || s[xx][yy] == '*' || vis[xx][yy]) continue ;
vis[xx][yy] = 1;
q.push(make_pair(xx, yy));
}
}
};
auto bfs2 = [&]() -> int {
int sum = 1E9;
for(int i = 0;i < (int) p.size(); i++) {
vector<vector<int>> dp(n, vector<int>(n, 1E9));
dp[p[i].first][p[i].second] = 0;
auto bfs = [&](int x, int y) {
queue<tuple<int, int, int>> q;
q.push(make_tuple(x, y, 0));
while(!q.empty()) {
int x, y, h;
tie(x, y, h) = q.front();
q.pop();
for(int j = 0;j < 4; j++) {
int xx = x + dx[j];
int yy = y + dy[j];
if(xx < 0 || xx >= n || yy < 0 || yy >= n || s[xx][yy] == '*' || dp[xx][yy] != 1E9) continue ;
dp[xx][yy] = min(dp[xx][yy], h + 1);
q.push(make_tuple(xx, yy, h + 1));
}
}
};
bfs(p[i].first, p[i].second);
int temp = 0;
for(int j = 0;j < (int) p.size(); j++) {
if(s[p[j].first][p[j].second] == '#') {
temp += dp[p[j].first][p[j].second];
}
}
sum = min(sum, temp);
}
return sum;
};
int ans = 0, cnt = 0;
for(int i = 0;i < n; i++) {
for(int j = 0;j < n; j++) {
if(s[i][j] == '*' || vis[i][j]) continue ;
p.clear();
flag = 0;
vis[i][j] = 1;
bfs1(i, j);
if(!flag) continue ;
cnt++;
ans += bfs2();
}
}
cout << cnt << " " << ans << endl;
} 思路:先求区域,再在区域基础上求距离。
用例都过了,提交过1/5,烦请各位大佬看看有哪里搞错了,万分感谢!
#include
#include
#include
#include
using namespace std;
// 思路:首先求可连通区域,然后再在联通区域内求距离的最小值。
// BFS求联通区域内的房子和道路,cmap中存房子,cmap1中存路。
void BFS(vector& mm, int i , int j, vector>& cmap, vector>& cmap1){
if (i = mm.size() || j = mm.size()) return;
if (mm[i][j] == '*') return;
if (mm[i][j] == '#') {
cmap.push_back({i, j}); // 存房子
}
else if (mm[i][j] == '.'){
cmap1.push_back({i, j}); // 存路
}
mm[i][j] = '*';
BFS(mm, i + 1, j, cmap, cmap1);
BFS(mm, i - 1, j, cmap, cmap1);
BFS(mm, i, j + 1, cmap, cmap1);
BFS(mm, i, j - 1, cmap, cmap1);
}
// 超市可以建在路和房子上,所以cmap和cmap1都是超市位置的候选,但只要求房子到超市的距离最小,内层循环一定遍历房子(cmap)
int disCalc(vector>& cmap, vector>& cmap1){
int minD = INT32_MAX;
for (int i = 0; i < cmap.size(); i++){
int dis = 0;
for (int j = 0; j < cmap.size(); j++){
dis += abs(cmap[i][0] - cmap[j][0]) + abs(cmap[i][1] - cmap[j][1]); // 求距离
}
if (dis < minD){
minD = dis;
}
}
for (int i = 0; i < cmap1.size(); i++){
int dis = 0;
for (int j = 0; j < cmap.size(); j++){
dis += abs(cmap1[i][0] - cmap[j][0]) + abs(cmap1[i][1] - cmap[j][1]);
}
if (dis < minD){
minD = dis;
}
}
return minD;
}
void deal(int n, vector& mat){
vector mm(mat.begin(), mat.end());
int cnt = 0;
int disSum = 0;
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (mm[i][j] != '*'){
vector> cmap;
vector> cmap1;
BFS(mm, i, j, cmap, cmap1);
if (cmap.size() == 0){ // 如果此联通区域内无房子,自然不用再计算距离和个数
continue;
}
else{
int dis = disCalc(cmap, cmap1);
disSum += dis;
cnt ++;
}
}
}
}
cout << cnt << " " << disSum << endl;
}
int main(){
int n;
cin >> n;
vector mat(n);
for (int i = 0; i < n; i++){
cin >> mat[i];
}
deal(n, mat);
return 0;
}
3 #*# [1,1][1,2][1,3] .** 对应的坐标 [2,1][2,2][2,3] *.# [3,1][3,2][3,3]可分成三个被隔离的通行区域
#include<iostream>
#include<vector>
#include<deque>
using namespace std;
// 区域大小
int N;
// 区域地图
char ts[50][50];
// 区域是否被访问过
// 用于第一步找出所有被隔离的区域
// 初始化设置false,被访问过设置true
bool visit[50][50];
// 区域id
// 相同的区域id,表示他们是在同一块区域
// 例如[1,1] = 1,[1,2] = 1,表示[1,1][1,2]在同一块区域内
// 初始化 -1
int field[50][50];
// 设置区域id使用,改成结构体会不会好理解点?
class tile
{
public:
char ch; // '#' '.' '*'
int i; // 坐标
int j; // 坐标
tile(int _i, int _j, char _ch)
{
i = _i;
j = _j;
ch = _ch;
visit[j][i] = true;
}
};
// 用于穷举法,计算区域内的可通行位置到达每个房子的距离之和
class result
{
public:
bool hasSuper; // 是否有超市(有些被隔离区域内可能没有超市)
int minPath; // 区域内每个房子到达超市最小距离之和
result(bool h, int m)
{
hasSuper = h;
minPath = m;
}
};
// 题目说的要检查传参
bool isValidChar(char ch)
{
// # 房子
// . 道路
// * 障碍
return ch == '#' ||
ch == '.' ||
ch == '*';
}
// 设置由__t展开的区域的区域id
void fillField(tile __t, int fieldId)
{
// 广度遍历的算法思想
// 由__t为起点,依次遍历__t四周的所有位置
// 可通行(道路或者房子),且没有被遍历过,纳入队列继续遍历
// 被遍历过的所有可通行位置,就属于同一个区域。
deque<tile> d;
d.push_back(__t);
while(d.size() != 0)
{
tile head = d.front();
d.pop_front();
field[head.j][head.i] = fieldId;
// 左遍历
int i = head.i - 1;
int j = head.j;
if (i >= 0 && !visit[j][i])
{
tile t(i, j, ts[j][i]);
d.push_back(t);
}
// 右遍历
i = head.i + 1;
j = head.j;
if (i < N && !visit[j][i])
{
tile t(i, j, ts[j][i]);
d.push_back(t);
}
// 上遍历
i = head.i;
j = head.j - 1;
if (j >= 0 && !visit[j][i])
{
tile t(i, j, ts[j][i]);
d.push_back(t);
}
// 下遍历
i = head.i;
j = head.j + 1;
if (j < N && !visit[j][i])
{
tile t(i, j, ts[j][i]);
d.push_back(t);
}
}
}
// 穷举法找到最小距离之和
result* findMinFath(int fieldId)
{
// 先把房子坐标找出
int roomI[50] = {-1};
int roomJ[50] = {-1};
int index = 0;
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (field[j][i] == fieldId && ts[j][i] == '#')
{
roomI[index] = i;
roomJ[index++] = j;
}
}
}
if(index == 0)
// 区域内没有房子
return new result(false, 0);
result* r = NULL;
for (int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
if (field[j][i] == fieldId)
{
// 计算[j,i]位置到达所有房子的最小距离
int path = 0;
for(int xx = 0; xx < index; xx++)
{
int diffI = roomI[xx] - i;
int diffJ = roomJ[xx] - j;
path += (diffI >= 0 ? diffI : -diffI);
path += (diffJ >= 0 ? diffJ : -diffJ);
}
if(r == NULL)
r = new result(true, path);
else
r->minPath = min(r->minPath, path);
}
}
}
return r;
}
int main()
{
cin >> N;
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cin >> ts[j][i];
if(!isValidChar(ts[j][i]))
{
// 输入错误直接返回
cout << "0 0" << endl;
return 0;
}
}
}
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
// 不可通行位置直接设置true
bool pass = ts[j][i] == '.' || ts[j][i] == '#';
visit[j][i] = !pass;
}
}
for(int i = 0; i < N; i++)
{
// 初始化
for (int j = 0; j < N; j++)
field[j][i] = -1;
}
vector<tile> fields;
fields.clear();
int fieldId = 0;
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
// 找出被隔离区域
if(!visit[j][i])
{
tile t(i, j, ts[j][i]);
fields.push_back(t);
fillField(t, ++fieldId);
}
}
}
int path = 0;
int super = 0;
for(int i = 1; i <= fieldId; i++)
{
result* r = findMinFath(i);
if (r->hasSuper)
{
// 有房子需要建超市
super++;
path += r->minPath;
}
free(r);
}
cout << super;
cout << " ";
cout << path << endl;
return 0;
}