注意

  该博客是为了帮助同学学习,并非为了协助同学刷题,请读者保持自觉,请勿做CV工具人。另外为了节省篇幅,代码中不再写明#includeusing,如果遇到我没有声明的函数或类,那么就是某一个头文件中的函数,读者搜索“std + 名字”就能查到相关信息。

感谢HZA大佬帮忙提交测试代码

题干

  点击查看原题链接(AcWing)

题干配图

  图1是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m*n(m ≤ 50,n ≤ 50)个方块,每个方块可以有0 ~ 4面墙。

输入格式

  程序从标准输入设备读入数据。第一行是两个整数,分别是南北向、东西向的方块数。在接下来的输入行里,每个方块用一个数字(0 ≤ p ≤ 50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1, 1)的南墙同时也是方块(2, 1)的北墙。输入的数据保证城堡至少有两个房间。

输出格式

  城堡的房间数、城堡中最大房间所包括的方块数。结果显示在标准输出设备上。

输入样例

  在这里给出一组输入。例如:

4
7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13

输出样例

  在这里给出相应的输出。例如:

9
5

解题过程

  这道题大眼一看就是使用深度优先搜索(DFS)进行搜索,直接给出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//输入数据
//格式:[Y轴][X轴][四个方向]
//其中,四个方向分别为:
// 0 - left
// 1 - up
// 2 - right
// 3 - down
bool input[50][50][4];
//标记某个点是否被搜索过
bool vis[50][50];
int height, width;

int solve(int _x, int _y) {
static pair<int, int> all[] = {
{-1, 0}, {0, -1},
{1, 0}, {0, 1}
};
int result = 0;
stack<pair<int, int>> stack;
stack.emplace(_x, _y);
do {
auto node = stack.top(); stack.pop();
int x = node.first, y = node.second;
if (vis[y][x]) continue;
vis[y][x] = true;
++result;
for (int i = 0; i != 4; ++i) {
int nx = x + all[i].first;
int ny = y + all[i].second;
//检查点是否在地图内,防止越界
if (ny == -1 || nx == -1 || ny == height || nx == width) continue;
//检查两个房间是否连接,稳妥起见两个房间都检查
//(i + 2) % 4 就是取反方向
if (input[y][x][i] && input[ny][nx][(i + 2) % 4]) stack.emplace(nx, ny);
}
} while (!stack.empty());
return result;
}

int main() {
ios::sync_with_stdio(false);
int value;
cin >> height >> width;
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
cin >> value;
for (int i = 0; i != 4; ++i) {
input[y][x][i] = !((value >> i) & 1);
}
}
}
int amount = 0;
int maxValue = 0;
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
if (!vis[y][x]) {
++amount;
maxValue = max(maxValue, solve(x, y));
}
}
}
printf("%d\n%d", amount, maxValue);
return 0;
}

  广度优先搜索(BFS)也是可以的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//输入数据
//格式:[Y轴][X轴][四个方向]
//其中,四个方向分别为:
// 0 - left
// 1 - up
// 2 - right
// 3 - down
bool input[50][50][4];
//标记某个点是否被搜索过
bool vis[50][50];
int height, width;

int solve(int _x, int _y) {
static pair<int, int> all[] = {
{-1, 0}, {0, -1},
{1, 0}, {0, 1}
};
int result = 0;
queue<pair<int, int>> queue;
queue.emplace(_x, _y);
do {
auto node = queue.front(); queue.pop();
int x = node.first, y = node.second;
if (vis[y][x]) continue;
vis[y][x] = true;
++result;
for (int i = 0; i != 4; ++i) {
int nx = x + all[i].first;
int ny = y + all[i].second;
//检查点是否在地图内,防止越界
if (ny == -1 || nx == -1 || ny == height || nx == width) continue;
//检查两个房间是否连接,稳妥起见两个房间都检查
//(i + 2) % 4 就是取反方向
if (input[y][x][i] && input[ny][nx][(i + 2) % 4]) queue.emplace(nx, ny);
}
} while (!queue.empty());
return result;
}

int main() {
ios::sync_with_stdio(false);
int value;
cin >> height >> width;
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
cin >> value;
for (int i = 0; i != 4; ++i) {
input[y][x][i] = !((value >> i) & 1);
}
}
}
int amount = 0;
int maxValue = 0;
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
if (!vis[y][x]) {
++amount;
maxValue = max(maxValue, solve(x, y));
}
}
}
printf("%d\n%d", amount, maxValue);
return 0;
}

  这道题使用并查集也是可以写的,每次相连的时候尝试连接两个垂直的方向(比如左面和上面)即可,最后再用map统计结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
struct {
// 0 - left
// 1 - up
// 2 - right
// 3 - down
bool link[4]{};
//并查集下标
int index = 0;
} input[50][50];

int check[50*50];
int height, width;

int find(int index) {
if (index == check[index]) return index;
return check[index] = find(check[index]);
}

//尝试连接
//参数:
// x, y - 坐标
// hor - 是否是横向连接
void tryLink(int x, int y, bool hor) {
int direct = hor ? 0 : 1; //邻接方向
int nx = hor ? x - 1 : x; //相邻点的横坐标
int ny = hor ? y : y - 1; //相邻点的纵坐标
//边界检查
if (nx == -1 || ny == -1 || nx == width || ny == height) return;
//连接检查
if (input[y][x].link[direct] && input[ny][nx].link[direct + 2]) {
//合并
check[find(input[y][x].index)] = find(input[ny][nx].index);
}
}

void solve(int index) {
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
tryLink(x, y, true);
tryLink(x, y, false);
}
}
map<int, int> record;
int ans = 0;
for (int i = 0; i != index; ++i) ans = max(ans, ++record[find(i)]);
printf("%lu\n%d", record.size(), ans);
}

int main() {
ios::sync_with_stdio(false);
int value, index = 0;
cin >> height >> width;
for (int y = 0; y != height; ++y) {
for (int x = 0; x != width; ++x) {
cin >> value;
check[index] = index;
input[y][x].index = index++;
for (int i = 0; i != 4; ++i) {
input[y][x].link[i] = !((value >> i) & 1);
}
}
}
solve(index);
return 0;
}

小结

  这两大类方法看下来是并查集的逻辑更简单,不过搜索的代码更简短,到底用哪种方法就看个人喜好了。

数据问题

  经过测试,这三个代码均只在AcWing上通过了所有数据点,在PTAOnline Judge上均只通过了一个数据点,大概率是后面两个的测试数据有问题。