题目描述
输入输出样例
样例1
- 输入
5 100 2 0 0 1 1 2 2 3 3 4 4 0 0 1 0 1 0 1 0 0
- 输出
3
- 解释
样例2
- 输入
5 4 2 0 0 1 1 2 2 3 3 4 4 0 0 0 0 1 0 1 0 0
- 输出
0
- 解释
数据范围
题意描述 & 思路
-
有一张绿化图(蓝色)和藏宝图(橙色),其中绿化图很大(二维数组在限定的空间内无法存储),而藏宝图是绿化图中的一部分
对于绿化图和藏宝图,左下角的坐标为(0, 0)
,右上角的坐标是(L, L)
、(S, S)
,其实就是笛卡尔坐标系 -
题目保证藏宝图的左下角必定是树,因此可以从绿化图中的每一个树进行模拟,若该点
(x, y)
作为左下角,直到右上角(x+S, y+S)
与藏宝图完全一致,说明该点符合题意,方案数+1
-
还需要注意,模拟时要保证面积与藏宝图一致,不能越界
即:藏宝图是绿化图的一部分,符合题意的点,必须完全包含藏宝图
图中只有红色部分相等是不满足题意的 -
藏宝图的输入比较特殊,是从左上角开始输入,直到右下角
因此,若用g
来表示藏宝图,保存时,从g[S][0]
开始,一直到g[S][S]
,这是第一行,第二行从g[S-1][0]
到g[S-1][S]
,用两个for
循环即可for(int i=S; i>=0; --i) for(int j=0; j<=S; ++j) cin >> g[i][j];
-
模拟时,首先取得绿化图中树的坐标,在该坐标的基础上进行运算
用check(a, b)
函数表示(a, b)
在绿化图中是否是 “树”bool f = true; int a = tree.x, b = tree.y; for(int i=0; i<=k; ++i) { for(int j=0; j<=k; ++j) { // (a, b) 是绿化图的左下角 // (a + i, b + j) 表示绿化图中坐标相对于藏宝图的位置 // 若 g[i][j] (藏宝图中是 1)并且绿化图中不是 1,那么不满足条件,退出 if(g[i][j] && !check(a + i, b + j)) { f = false; break; } // 若 !g[i][j] (藏宝图中是 0),并且绿化图中是 1,那么不满足条件,退出 if(!g[i][j] && check(a + i, b + j)) { f = false; break; } } if(!f) break; }
-
check
函数的实现,可以使用set
,保存输入数据中的x, y
typedef pair<int, int> PII; // t 中存储绿化图中所有的树 set <PII> t; for(int i=0; i<n; ++i) { int a, b; cin >> a >> b; t.insert({a, b}); }
代码
- 运行结果
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
const int N = 1010, M = 55;
// n : 树的个数
// m : 绿化图的大小
// k : 藏宝图的大小
// res : 符合题意的方案数
int n, m, k, res;
// 藏宝图
int g[N][N];
// 绿化图中树的坐标
set <PII> t;
int main()
{
cin >> n >> m >> k;
for(int i=0; i<n; ++i)
{
int a, b;
cin >> a >> b;
t.insert({a, b});
}
for(int i=k; i>=0; --i)
for(int j=0; j<=k; ++j)
cin >> g[i][j];
for(auto& tree : t)
{
bool f = true;
int a = tree.x, b = tree.y;
if(a + k > m || b + k > m) continue;
for(int i=0; i<=k; ++i)
{
for(int j=0; j<=k; ++j)
{
if(g[i][j] && !t.count({a + i, b + j}))
{
f = false;
break;
}
if(!g[i][j] && t.count({a + i, b + j}))
{
f = false;
break;
}
}
if(!f) break;
}
if(f) ++res;
}
cout << res << endl;
}