2023蓝桥杯C++B组省赛F题:岛屿个数
#include<iostream>
#include<algorithm>
#include<queue>using namespace std;
typedef long long ll;const int dx[8] = { 1, -1, 0, 0, -1, -1, 1, 1 };
const int dy[8] = { 0, 0, 1, -1, -1, 1, -1, 1 };/* 地图块周围的八块格子 */
int n, m;
char graph[60][60];/*实际图*/
bool vis[60][60];/*视图*/bool fill(int A, int B) { // 传入第 A 行第 B 列的地图块for (int i = 0; i <= n + 1; i++) {for (int j = 0; j <= m + 1; j++) {vis[i][j] = false; //给视图层地图赋值,视图层地图比实际地图大一圈}}queue<pair<int, int> > q; //元素为对组类型数据的队列q.emplace(A, B); //将该陆地块位置添加到队列末尾vis[A][B] = true; //视图层标记传入陆地块while (!q.empty()) {auto cur = q.front(); //auto修饰的变量自动选择合适的数据类型(对组),cur存入队列中数据q.pop();//出队操作int x = cur.first, y = cur.second;//x,y分别存储陆地块的行、列for (int i = 0; i < 8; i++) {int nx = x + dx[i], ny = y + dy[i];/* nx , ny 存储了传入陆地块周围八个格子的数据*/if (nx < 1 || nx > n || ny < 1 || ny > m) return true;//判断该陆地是否为边缘块,是则返回trueif (graph[nx][ny] == '0' && !vis[nx][ny]) { //若该周围块为海洋且对应视图块未标记vis[nx][ny] = true; //标记该视图块q.emplace(nx, ny); //队列中传入该视图块,下一次循环将监视该标记块的周围块}}}return false;//遍历结束,所有延伸标记的块均不为边缘块,则返回false
}void bfs(int A, int B) {queue<pair<int, int> > q;q.emplace(A, B);while (!q.empty()) {auto cur = q.front();q.pop();int x = cur.first, y = cur.second;for (int i = 0; i < 4; i++) {//利用队列广度搜索上下左右的块int nx = x + dx[i], ny = y + dy[i];if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && graph[nx][ny] == '1') {//周围衔接陆地块graph[nx][ny] = '0';//陆地块变海洋块q.emplace(nx, ny);//周围块入队}}}
}int main() {ios::sync_with_stdio(false);int T;cin >> T;while (T--) {cin >> n >> m; //确定地图大小:n 行 m 列for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {cin >> graph[i][j];}} //输入地图数据int ans = 0; // ans 表示答案for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (graph[i][j] == '1') { //遍历地图,判断当前块是否为陆地if (!fill(i, j)) //fill函数:遍历该陆地块周围所有海洋,如果海水最终能漏出到地图边界,说明该陆地并没有被某个环包围,返回truegraph[i][j] = '0'; //将被环包围的陆地变为海洋,这样子岛屿将不复存在}}}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (graph[i][j] == '1') {//若再次遍历到陆地,则岛屿数+1ans++;bfs(i, j);//利用广度搜索删除掉该陆地块周围衔接的所有陆地块,这样地图上每个陆地块最终都只代表一个岛屿!}}}cout << ans << endl;}return 0;
}
这道题的解法妙在判断岛屿是否被环包围,反其道而行之,改而分析周围的海洋能否衍生到地图边界。
2023蓝桥杯C++B组省赛H题:整数删除
#include <iostream>
#include <algorithm>
#include <queue>using namespace std;
typedef long long ll;const int maxn = 5e5 + 10;ll v[maxn], l[maxn], r[maxn];void del(ll x) { //传入要删除的序号r[l[x]] = r[x], l[r[x]] = l[x]; //删除结点前驱的后继指向删除结点的后继,删除结点后继的前驱指向删除结点的前驱v[l[x]] += v[x], v[r[x]] += v[x]; //删除结点的两边结点数值增加
}int main() {int n, k; //n为数列长度,重复k次cin >> n >> k;priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;/*priority_queue为优先级队列,greater表示数字小的优先级高*/r[0] = 1, l[n + 1] = n;for (int i = 1; i <= n; i++) {cin >> v[i]; //v[1~n]用于存储数列l[i] = i - 1; //存储对应序号数字的左边一位,对应结点i的前驱r[i] = i + 1; //存储对应序号数字的右边一位,对应结点i的后继pq.emplace(v[i], i); //将数字和它在数列中的序号传入队列}while (k--) {auto p = pq.top(); //获取堆顶(队首)元素,即输入的最小数和其序号pq.pop(); //删除堆顶(队首)元素if (p.first != v[p.second]) {pq.emplace(v[p.second], p.second);k++;} //当原本的最小数因为吸收删除数值变大后,将其重新入队else del(p.second); //否则进行删除结点操作}ll head = r[0];while (head != n + 1) {cout << v[head] << " "; //输出最终结果数列head = r[head]; //访问下一个数}return 0;
}
这道题巧妙地构造了两个数组r[ ] 和l[ ],充当了指针的作用,全程序看似没有使用链表,实则处处使用链表。