CCF-CSP 202206-2 寻宝!大冒险!
样例输入1
5 100 2
0 0
1 1
2 2
3 3
4 4
0 0 1
0 1 0
1 0 0
第一行三个数分别为 n , L n,L n,L 和 S S S ,分别表示西西艾弗岛上树的棵数、绿化图和藏宝图的大小。
接下来 n n n 行为 n n n 棵树的坐标。
最后 S + 1 S+1 S+1 行为藏宝图 B B B ,原点( B [ 0 ] [ 0 ] B[0][0] B[0][0] )位于左下角的位置。
样例1示意图如下所示。
易得,藏宝图 B B B 左下角和绿化图的重合的坐标有3处。
样例输入2
5 4 2
0 0
1 1
2 2
3 3
4 4
0 0 0
0 1 0
1 0 0
样例2的示意图如下所示。可以发现,当藏宝图
B
B
B 和坐标
(
3
,
3
)
(3,3)
(3,3) 重合时,藏宝图已经超出绿化图
A
A
A 的边界了,因此该解不符合题意,舍去。输出
0
0
0 。
解题思路
全部的测试数据满足: n ≤ 1000 、 L ≤ 1 0 9 n\leq1000、L\leq10^9 n≤1000、L≤109 且 S ≤ 50 S\leq50 S≤50 。
由于绿化图的边长数量级可达
1
0
9
10^9
109 ,所以不适合用数组来存储绿化图
A
A
A ,可以采用 map<int,set<int>> myMap
的数据结构来对树木的坐标进行存储。map的第一个键值为树木的x坐标,keyword对应的值为一个set集合,存储的是该行下,所有树木的列坐标。
因为藏宝图的边长 ( S + 1 ) ≤ 51 (S+1)\leq51 (S+1)≤51 ,因此,可直接用数组对藏宝图进行存储。
根据题意,绿化图 A A A 中存在着一处坐标 ( x , y ) ( 0 ≤ x , y ≤ L − S ) (x,y)(0\leq x,y \leq L-S) (x,y)(0≤x,y≤L−S) 与藏宝图 B B B 左下角 ( 0 , 0 ) (0,0) (0,0) 相对应,即满足对 B B B 上任意一处坐标 ( i , j ) ( 0 ≤ i , j ≤ S ) (i,j) (0\leq i,j \leq S) (i,j)(0≤i,j≤S) 都有 A [ x + i ] [ y + i ] = B [ i ] [ j ] A[x+i][y+i]=B[i][j] A[x+i][y+i]=B[i][j] 。则可以遍历绿化图 A A A 中树的坐标,检查对每一棵树,是否满足上述条件。
满分题解
#include <iostream>
using namespace std;
#include <set>
#include <map>
#include <algorithm>
map<int,set<int>> myMap;
int B[51][51];
int main(int argc, char const *argv[])
{
int n,L,S;
int row,col;
cin >> n >> L >> S;
for (int i = 0; i < n; i++){
cin >> row >> col;
myMap[row].insert(col);
}
// 藏宝图
for (int i = 0; i <= S; i++){
for (int j = 0; j <= S; j++){
cin >> B[S-i][j];
}
}
int ans = 0;
bool flag;
for (auto row:myMap){
for (auto col:row.second){
// 取到了地图上的一个点
// 判断该点是否可以作为藏宝图的原点
flag = 0;
for (int i = 0; i <= S; i++){
for (int j = 0; j <= S; j++){
if (i+row.first>L){
flag = 1; // 越界
break;
}
if (B[i][j]){
if (myMap[i+row.first].find(j+col) == myMap[i+row.first].end()){
flag = 1; // 不符合条件
break;
}
}
else{
if (myMap[i+row.first].find(j+col) != myMap[i+row.first].end()){
flag = 1; // 不符合条件
break;
}
}
}
}
if (!flag){
ans++;
}
}
}
cout << ans;
return 0;
}