寻宝!大冒险!
枚举树作为左下角,检查平移后的藏宝图 1 集合与窗口内树集合完全相同。
OJ: shumeng
题目 ID: CSP202206B
难度:普及-
标签:模拟集合网格
日期: 2026-07-31 16:21
形式化题目
有一张大小为
问有多少个左下角
思路
藏宝图很小(
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:40
*/
// brute.cpp:小数据暴力解,枚举每个候选左下角并逐格比较完整藏宝图。
#include <bits/stdc++.h>
using namespace std;
int n;
long long limit;
int s;
vector<pair<long long, long long> > tree;
set<pair<long long, long long> > exists; // 用集合记录每棵树的位置
vector<vector<int> > map_value;
int answer;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> limit >> s;
tree.resize(n);
for (int i = 0; i < n; i++) {
cin >> tree[i].first >> tree[i].second;
exists.insert(tree[i]);
}
map_value.assign(s + 1, vector<int>(s + 1, 0));
for (int row = s; row >= 0; row--) {
for (int col = 0; col <= s; col++) {
cin >> map_value[row][col];
}
}
// 对每个树作为左下角,展开整个 (S+1)*(S+1) 窗口逐格比较
for (int i = 0; i < n; i++) {
long long x = tree[i].first, y = tree[i].second;
if (x + s > limit || y + s > limit) continue;
bool valid = true;
for (int dx = 0; dx <= s && valid; dx++) {
for (int dy = 0; dy <= s; dy++) {
bool has_tree = exists.count({x + dx, y + dy}) != 0;
if (has_tree != (map_value[dx][dy] != 0)) {
valid = false;
break;
}
}
}
if (valid) answer++;
}
cout << answer << '\n';
return 0;
}这个做法逐格比较需要
关键观察
- 藏宝图左下角一定是树,所以候选左下角只可能是
棵树之一。 - 对候选
,窗口内实际存在的树必须是藏宝图中为 1 的位置。 - 反之,藏宝图中为 1 的位置也必须在窗口内有树。
第二个方向只需检查数量:窗口内的树总数等于藏宝图中 1 的总数,即可保证 1 的位置都有树。
算法步骤
- 读入
棵树的位置。 - 读入藏宝图,注意输入按从上到下给出,实际存储时把行号倒置,让
map[dx][dy]与坐标偏移对应;同时统计藏宝图中 1 的个数 tree_count。 - 枚举每棵树作为候选左下角,先排除窗口越界的候选。
- 遍历所有树,统计落在窗口内的树,并检查每棵窗口内的树对应藏宝图位置是否为 1。
- 若窗口内树数等于
tree_count,则该候选匹配,答案加一。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:40
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n; // 树的数量
long long limit; // 绿化图大小 L
int s; // 藏宝图大小 S
pair<long long, long long> tree[MAXN]; // 每棵树的坐标 (x, y)
bool map_value[55][55]; // 藏宝图:map[dx][dy] 表示相对左下角的偏移位置是否有树
int tree_count; // 藏宝图中 1 的总数
int answer; // 匹配的左下角候选个数
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> limit >> s;
for (int i = 0; i < n; i++) {
cin >> tree[i].first >> tree[i].second;
}
// 藏宝图按从下往上逐行读入,所以先读最上面一行,再填到 map[0..s][0..s]
// 这里把输入的第 s 行放到 map 的第 s 行,保证 map[dx][dy] 与坐标偏移一致
for (int row = s; row >= 0; row--) {
for (int col = 0; col <= s; col++) {
cin >> map_value[row][col];
if (map_value[row][col]) tree_count++;
}
}
// 枚举每个树作为藏宝图左下角,检查该窗口内的树是否与藏宝图一致
for (int i = 0; i < n; i++) {
long long x = tree[i].first, y = tree[i].second;
if (x + s > limit || y + s > limit) continue; // 窗口越出绿化图边界
bool valid = true;
int inside = 0; // 落在窗口内的树的数量
for (int j = 0; j < n; j++) {
long long tx = tree[j].first, ty = tree[j].second;
if (tx < x || tx > x + s || ty < y || ty > y + s) continue;
inside++;
if (!map_value[tx - x][ty - y]) { // 窗口内有树但藏宝图对应位置为 0
valid = false;
break;
}
}
// 窗口内树的数量必须恰好等于藏宝图中 1 的数量,
// 这样藏宝图为 1 的位置也都一定有树。
if (valid && inside == tree_count) answer++;
}
cout << answer << '\n';
return 0;
}复杂度
枚举
总结
稀疏地图不需要展开到