acwing 1106.山峰和山谷 & 博客新起点
受kkzz1x的激励,决定保持每天产出一篇博客记录自己的学习~
打算捡起AcWing的算法提高课按照课程刷下去,但是一定不能是被动的学习,而要积极的思考。
这两天写了密码学实验和一道Flood Filled。许久未写代码,惊讶地发现自己的代码实现能力一泻千里,接下来必须保持练习。
1106 这道题就是用 bfs 解,但在应用算法时,大致思路容易确定,具体细节难以一步到位,接下来需要刻意练习这方面的能力。
丑陋的代码:
// Problem: 山峰和山谷
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1108/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
/*
思路:
1. 遍历每个单元格
2. 执行 BFS
2.1 记录是山峰还是山谷
2.2 拓展至八个方向
2.3 判断是否仍为山峰或者山谷
2.4 该标记的标记
*/
#include<iostream>
#include<queue>
using namespace std;
typedef pair<int, int> pp;
#define x first
#define y second
const int N = 1005;
int n;
pp Q[N*N];
int map[N][N], st[N][N];
int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
int main(void){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < n; j ++ ) {
cin >> map[i][j];
}
}
int hCnt = 0, lCnt = 0;
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < n; j ++ ) {
bool H = 0, L = 0;
int hh = 0, tt = 0;
if (st[i][j]) continue;
st[i][j] = true;
Q[0] = {i,j};
while(hh <= tt){
pp tep = Q[hh ++ ];
int x = tep.x;
int y = tep.y;
st[x][y] = 1;
for (int i = 0; i < 8; i ++ ) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || ny < 0 || nx >= n || ny >= n )
continue;
if (map[x][y] < map[nx][ny]) L = true ;
if (map[x][y] > map[nx][ny]) H = true ;
if (map[x][y] == map[nx][ny] && !st[nx][ny]) {
Q[++ tt] = {nx, ny};
st[nx][ny] = 1;
}
}
}
if (H == 0) lCnt ++ ;
if (L == 0) hCnt ++ ;
// printf("i: %d, j: %d, h: %d, l: %d \n\n", i, j, H, L);
}
}
cout << hCnt << " " << lCnt;
return 0;
}
优雅的代码(yxc):
// Problem: 山峰和山谷
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1108/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
/*
思路:
1. 遍历每个单元格
2. 执行 BFS
2.1 记录是山峰还是山谷
2.2 拓展至八个方向
2.3 判断是否仍为山峰或者山谷
2.4 该标记的标记
*/
#include<iostream>
#include<queue>
using namespace std;
typedef pair<int, int> pp;
#define x first
#define y second
const int N = 1005;
int n;
pp Q[N*N];
int map[N][N], st[N][N];
int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
void bfs(int sx, int sy, bool& H, bool& L) {
int hh = 0, tt = 0;
Q[0] = {sx,sy};
st[sx][sy] = true;
while(hh <= tt){
pp tep = Q[hh ++ ];
int x = tep.x;
int y = tep.y;
st[x][y] = 1;
for (int i = 0; i < 8; i ++ ) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || ny < 0 || nx >= n || ny >= n )
continue;
if (map[x][y] != map[nx][ny]){
if (map[x][y] > map[nx][ny]) H = true;
if (map[x][y] < map[nx][ny]) L = true;
}
else if (!st[nx][ny]) {
st[nx][ny] = 1;
Q[++ tt] = {nx, ny};
}
}
}
}
int main(void){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < n; j ++ ) {
cin >> map[i][j];
}
}
int hCnt = 0, lCnt = 0;
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < n; j ++ ) {
if (st[i][j]) continue;
bool H = 0, L = 0;
bfs(i, j, H, L);
if (H == 0) lCnt ++ ;
if (L == 0) hCnt ++ ;
}
}
cout << hCnt << " " << lCnt;
return 0;
}
教训
- Flood filled 算法不要用 DFS
- BFS 也要包装成函数的形式(而不是循环套循环)
- 不要用 STL 的队列写 BFS
- st 数组要放好位置
- 已访问的不进行 bfs
- 进入 bfs 了,便标记已访问
- 加入队列之前,要判断是否已访问,并进行标记
- 队列的大小不能是 N,得是 N*N
- 只判断是否为0的变量就用布尔值