#P2699. 第2题-亲密部落
-
1000ms
Tried: 26
Accepted: 11
Difficulty: 4
所属公司 :
阿里
时间 :2025年3月15日-阿里淘天(开发岗)
-
算法标签>BFS
第2题-亲密部落
题解
题面描述
国家的领土被看作由 n 行 m 列构成的矩阵,每个单元格中都有一个部落,用小写字母表示,记为 si,j。
若同一部族的任意两个相邻(四相邻,即上下左右)的单元格直接连接,则这些单元格构成一个 亲密部落。
对于任意一个单元格,要求计算其所在的亲密部落与多少个不同的非本部族的部落(敌对部落)相邻。
输入给定 n,m 以及 n 行字符串,每行长度为 m,输出同样 n 行,每行 m 个整数,第 i 行第 j 个整数表示 (i,j) 单元格所在的亲密部落与多少个不同敌对部落相邻。
思路
本题利用图论中的连通域思想,通过深度优先搜索或广度优先搜索,将相同部族且四相邻的单元格划分到同一亲密部落中,同时在遍历过程中记录每个连通域边界上与之相邻的不同部族,最终统计出每个连通域的敌对部落数量,并输出对应于每个单元格的结果。
-
连通域划分
利用 深度优先搜索 (DFS) 或 广度优先搜索 (BFS) 对网格进行遍历,将同一部族且通过四相邻相连的单元格划分到同一个连通域,也就是同一个亲密部落。
设 id[i][j] 表示 (i,j) 所在的连通域编号,同时记录每个连通域的部族字母。 -
敌对部落统计
对每个连通域,在遍历过程中考察其边界邻居(相邻的单元格),若相邻单元格属于不同的部族,则将该部族记录到该连通域的集合中。
最后,每个连通域的敌对部落数量即为集合中不同部族的数量。 -
结果输出
对于每个单元格,直接输出其所在连通域的敌对部落数量。
时间复杂度为 O(n×m),适用于 n,m≤500。
C++
#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<string> grid(n);
for (int i = 0; i < n; i++){
cin >> grid[i];
}
// 用于标记每个单元格所属的连通域编号,初始值为 -1 表示未访问
vector<vector<int>> comp(n, vector<int>(m, -1));
// 保存每个连通域敌对部落数量
vector<int> enemyCount;
// 保存每个连通域的部族字符
vector<char> tribe;
int compId = 0;
// 四个方向的移动
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
// 使用 BFS 进行连通域搜索
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
if(comp[i][j] == -1){
// 当前连通域的部族
char currentTribe = grid[i][j];
// 保存该连通域中相邻不同部族的集合
set<char> enemySet;
// BFS 队列
queue<pair<int,int>> q;
q.push({i, j});
comp[i][j] = compId;
while(!q.empty()){
auto cur = q.front();
q.pop();
int x = cur.first, y = cur.second;
// 遍历四个方向
for (int d = 0; d < 4; d++){
int nx = x + dx[d];
int ny = y + dy[d];
if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
// 如果相邻单元格与当前单元格属于同一部族且未被访问,则加入队列
if(grid[nx][ny] == currentTribe && comp[nx][ny] == -1){
comp[nx][ny] = compId;
q.push({nx, ny});
}
// 如果部族不同,则记录敌对部落
else if(grid[nx][ny] != currentTribe){
enemySet.insert(grid[nx][ny]);
}
}
}
// 保存该连通域的敌对部落数量
enemyCount.push_back(enemySet.size());
tribe.push_back(currentTribe);
compId++;
}
}
}
// 输出答案,每个单元格所在连通域的敌对部落数量
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
cout << enemyCount[comp[i][j]] << " ";
}
cout << "\n";
}
return 0;
}
Python
# -*- coding: utf-8 -*-
from collections import deque
def main():
import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.readline
# 读入 $n$ 和 $m$
n, m = map(int, input().split())
grid = [input().strip() for _ in range(n)]
# 用于标记每个单元格所属的连通域,初始值为 -1 表示未访问
comp = [[-1] * m for _ in range(n)]
enemyCount = [] # 每个连通域的敌对部落数量
tribe = [] # 每个连通域的部族字符
compId = 0
# 四个方向
directions = [(1,0), (-1,0), (0,1), (0,-1)]
# 使用 BFS 进行连通域搜索
for i in range(n):
for j in range(m):
if comp[i][j] == -1:
currentTribe = grid[i][j] # 当前连通域的部族
enemySet = set() # 保存相邻的不同部族
q = deque()
q.append((i, j))
comp[i][j] = compId
while q:
x, y = q.popleft()
# 遍历四个方向
for dx, dy in directions:
nx, ny = x + dx, y + dy
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
# 如果相邻单元格与当前部族相同且未访问,则加入队列
if grid[nx][ny] == currentTribe and comp[nx][ny] == -1:
comp[nx][ny] = compId
q.append((nx, ny))
# 如果部族不同,则记录敌对部落
elif grid[nx][ny] != currentTribe:
enemySet.add(grid[nx][ny])
enemyCount.append(len(enemySet))
tribe.append(currentTribe)
compId += 1
# 输出答案
for i in range(n):
res = []
for j in range(m):
res.append(str(enemyCount[comp[i][j]]))
print(" ".join(res))
if __name__ == "__main__":
main()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
// BufferedReader 用于快速输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] parts = br.readLine().split(" ");
int n = Integer.parseInt(parts[0]);
int m = Integer.parseInt(parts[1]);
char[][] grid = new char[n][m];
for (int i = 0; i < n; i++){
String line = br.readLine();
grid[i] = line.toCharArray();
}
// comp 数组用于标记每个单元格所属的连通域,初始值为 -1 表示未访问
int[][] comp = new int[n][m];
for (int[] row : comp)
Arrays.fill(row, -1);
// 存储每个连通域的敌对部落数量
ArrayList<Integer> enemyCount = new ArrayList<>();
// 存储每个连通域的部族字符
ArrayList<Character> tribe = new ArrayList<>();
int compId = 0;
// 定义四个方向
int[] dx = {1, -1, 0, 0};
int[] dy = {0, 0, 1, -1};
// 使用 BFS 进行连通域搜索
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
if(comp[i][j] == -1){
char currentTribe = grid[i][j];
HashSet<Character> enemySet = new HashSet<>();
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i, j});
comp[i][j] = compId;
while(!queue.isEmpty()){
int[] cur = queue.poll();
int x = cur[0], y = cur[1];
// 遍历四个方向
for (int d = 0; d < 4; d++){
int nx = x + dx[d];
int ny = y + dy[d];
if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if(grid[nx][ny] == currentTribe && comp[nx][ny] == -1){
comp[nx][ny] = compId;
queue.add(new int[]{nx, ny});
} else if(grid[nx][ny] != currentTribe){
enemySet.add(grid[nx][ny]);
}
}
}
enemyCount.add(enemySet.size());
tribe.add(currentTribe);
compId++;
}
}
}
// 输出答案
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
sb.append(enemyCount.get(comp[i][j])).append(" ");
}
sb.append("\n");
}
System.out.print(sb.toString());
}
}
题目内容
某国的领土可以看作n行m列的矩阵,一共n×m个单元格,每一个单元格中都有一个部落。
我们使用(i,j) 表示矩阵中从上往下数第i行和从左往右数第j列的单元格,里面的部族名为小写字母si,j
同一个部族的成员如果彼此相邻,那么他们更进一步的被称作亲密部落。更具体地,若存在这样两个单元格(x,y) 和(p,q),使得 sx,y =sp,q且∣x−p∣+∣y−q∣=1,那么我们称他们属于同一个亲密部落。
现在小红想让你回答,对于任意(i,j)这个单元格上的部族,其所在亲密部落与多少个非自己部族的部族相邻。
在本题中,我们认为两个单元格(x,y)和(p,q) 是相邻的,当且仅当∣x−p∣+∣y−q∣=1(四相邻)。
输入描述
第一行两个整数n,m(1≦n,m≦500)代表国家的领土大小。
此后n行,第i行输入一个长度为m,由小写字母组成的字符串si,代表第i行的部落分布。
输出描述
输出n行,每行m个整数,第i行第j个整数代表(i,j)所在部落存在多少不同的敌对部落。
样例1
输入
3 3
baa
aab
cac
输出
1 2 2
2 2 2
1 2 2
说明
在这个样例中,以(1,2)这个单元格上的'a'部落为例,其所在的亲密部落共包含5个单元格,分别是(1,2),(1,3),(2,1),(2,2),(3,2)。与'b'、'a'部落相邻 自5个