蓝桥-13届-C++-B组-省赛-H题-扫雷
直达链接
感觉是很有意思的题目——如果我会做的话
模拟吗?模拟这个过程,怎么标记一颗雷已经被排除呢?三个元素的存储可能不得不一个二维数组,既然这样,干脆就添加一个标志位:是否已经排除(爆炸),但是每次都遍历的话,复杂度O(N*M)肯定超时
5个过,3个错,12个超时
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
int cnt = 0;
vector<vector<int>> landmine;
long long range;
// 因为存在递归,所以不得不单独抽一个函数出来
void boom(int x, int y, int r) {
for (int i = 0; i < landmine.size(); i++) {
if (cnt == landmine.size()) break;
range = pow(r, 2);
// 注意这里的引爆不是双向的,A爆了能引爆B,但是B爆了不一定能引爆A
if (landmine[i][0] == 0) {
if (pow(x - landmine[i][1], 2) + pow(y - landmine[i][2], 2) < range) {
cnt++;
landmine[i][0]++;
boom(landmine[i][1], landmine[i][2], landmine[i][3]);
}
}
}
}
int main() {
int n, m;
cin >> n >> m;
// 我们假设第一位是一个额外添加的标志位,0未排除,1已排除
vector<int> temp(4, 0);
int x, y, r;
// 获取所有的地雷列表
for (int i = 0; i < n; i++) {
cin >> x >> y >> r;
temp[1] = x;
temp[2] = y;
temp[3] = r;
landmine.push_back(temp);
}
// 但是每次不管咋样,都要遍历一次地雷列表,检查状态并操作,是不是时间复杂度太高了
for (int k = 0; k < m; k++) {
cin >> x >> y >> r;
boom(x, y, r);
}
cout << cnt;
return 0;
}
关于超时的问题,关键是有没有可能在遍历递归的过程中,更改(删除元素)集合的容量
好我解决了3个错误的问题,在于<= range
这里在边线上也是会被引爆的,接下来就是超时的问题
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
vector<vector<int>> landmineRow;
int cnt = 0;
int range;
// 因为存在递归,所以不得不单独抽一个函数出来
void boom(int x, int y, int r, vector<vector<int>>& landmine) {
vector<int> boomed;
vector<vector<int>> leftLanmine;
for (int i = 0; i < landmine.size(); i++) {
range = pow(r, 2);
// 注意这里的引爆不是双向的,A爆了能引爆B,但是B爆了不一定能引爆A
if (pow(x - landmine[i][0], 2) + pow(y - landmine[i][1], 2) <= range) boomed.push_back(i);
else leftLanmine.push_back(landmine[i]);
}
landmineRow.assign(leftLanmine.begin(), leftLanmine.end());
cnt += boomed.size();
if (landmineRow.size() == 0) return;
for (int i : boomed) boom(landmine[i][0], landmine[i][1], landmine[i][2], landmineRow);
}
int main() {
int n, m;
cin >> n >> m;
// 我们假设第一位是一个额外添加的标志位,0未排除,1已排除
vector<int> temp(3, 0);
int x, y, r;
// 获取所有的地雷列表
for (int i = 0; i < n; i++) {
cin >> x >> y >> r;
temp[0] = x;
temp[1] = y;
temp[2] = r;
landmineRow.push_back(temp);
}
// 但是每次不管咋样,都要遍历一次地雷列表,检查状态并操作,是不是时间复杂度太高了
for (int k = 0; k < m; k++) {
cin >> x >> y >> r;
boom(x, y, r, landmineRow);
}
cout << cnt;
return 0;
}
我努力尝试解决超时问题,但是问题没解决,反而一个都不对了,果然我还是该睡觉了,效率、状态太差
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
int range;// r的最大值也就是10,也就是说平方不超过100
// 因为存在递归,所以不得不单独抽一个函数出来
void boom(int x, int y, int r, vector<vector<int>>& landmine) {
if (landmine.size() == 0) return;
vector<int> boomed;
vector<vector<int>> leftLandmine;
for (int i = 0; i < landmine.size(); i++) {
range = pow(r, 2);
// 注意这里的引爆不是双向的,A爆了能引爆B,但是B爆了不一定能引爆A
if (pow(x - landmine[i][0], 2) + pow(y - landmine[i][1], 2) <= range) boomed.push_back(i);
else leftLandmine.push_back(landmine[i]);
}
for (int i : boomed) boom(landmine[i][0], landmine[i][1], landmine[i][2], leftLandmine);
landmine.assign(leftLandmine.begin(), leftLandmine.end());
}
int main() {
int n, m;
cin >> n >> m;
vector<int> temp(3, 0);
int x, y, r;
vector<vector<int>> landmineRow;
for (int i = 0; i < n; i++) {
cin >> x >> y >> r;
temp[0] = x;
temp[1] = y;
temp[2] = r;
landmineRow.push_back(temp);
}
for (int k = 0; k < m; k++) {
cin >> x >> y >> r;
boom(x, y, r, landmineRow);
}
cout << n - landmineRow.size();
return 0;
}
啊,我真的尽力了,怎么还是12个超时,超时一个没解决,我都已经动态更新地雷列表了
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
vector<vector<int>> landmineRow;
int range;// r的最大值也就是10,也就是说平方不超过100
// 因为存在递归,所以不得不单独抽一个函数出来
void boom(int x, int y, int r, vector<vector<int>>& landmine) {
if (landmine.size() == 0) return;
vector<int> boomed;
vector<vector<int>> leftLandmine;
for (int i = 0; i < landmine.size(); i++) {
range = pow(r, 2);
// 注意这里的引爆不是双向的,A爆了能引爆B,但是B爆了不一定能引爆A
if (pow(x - landmine[i][0], 2) + pow(y - landmine[i][1], 2) <= range) boomed.push_back(i);
else leftLandmine.push_back(landmine[i]);
}
for (int i : boomed) boom(landmine[i][0], landmine[i][1], landmine[i][2], leftLandmine);
// 这里的更新是给下一次除雷火箭用的,而不是给递归本身用的,递归本身用的是leftLandmine,这两个过程都已经是动态更新了为什么还是超时
for (int i : boomed) landmineRow.erase(landmineRow.begin() + i);// 为什么这么写不对?不是和下面那句等价吗
// landmine.assign(leftLandmine.begin(), leftLandmine.end());// 我觉得这句不对,上面那句是对的
}
int main() {
int n, m;
cin >> n >> m;
vector<int> temp(3, 0);
int x, y, r;
for (int i = 0; i < n; i++) {
cin >> x >> y >> r;
temp[0] = x;
temp[1] = y;
temp[2] = r;
landmineRow.push_back(temp);
}
for (int k = 0; k < m; k++) {
cin >> x >> y >> r;
boom(x, y, r, landmineRow);
}
cout << n - landmineRow.size();
return 0;
}
不行了,超时怎么解决,是不是只有换思路了,模拟必定超时
分析60%超时正好是题目给出的测试用例比例,也就是说我能过103但是过不了5*104
2023/3/7
题解得思路是这样的:
因为n的数量高达5*104,所以考虑连锁反应的话必定是超时的(但是不可能不判断吧,万一就有会被雷引爆而不是被火箭引爆的呢?)
#include<iostream>
#include<map>
#include<queue>
using namespace std;
int n, m;
// C用来给每个点的雷数量计数,R用来记录每个点位置雷的最大半径
map<pair<int, int>, int> R, C;
/*
* 计算两点之间的距离
*/
int distanse(int x1,int y1,int x2,int y2) {
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}
/*
* 多源点BFS算法
*/
int BFS() {
int ans = 0;
// 这是个什么数据结构?将坐标点pair和半径组成一个新pair,然后再构造一个队列
queue<pair<pair<int, int>, int>> Q;
/*
* 获取输入并构造火箭队列
*/
for (int i = 0; i < m; i++) {
int x, y, r;
cin >> x>> y>>r;
auto point = make_pair(x, y);
Q.push(make_pair(point, r));
}
while (!Q.empty()) {
auto node = Q.front();
Q.pop();
int x = node.first.first, y = node.first.second, r = node.second;
/*
* 遍历以xy为圆心,r为半径的圆中的所有点
* 注意这里要包括在边界上的点
* 因为这里最多r=10,所以双层循环的最高复杂度也只有400
*/
for (int i = x - r; i <= x + r; i++) {
for (int j = y - r; j <= y + r; j++) {
if (distanse(i, j, x, y) > r*r) continue;// 如果距离在引爆范围外
auto point = make_pair(i, j);
/*
* 如果该点存在炸雷,就记下该点的炸雷数
* 该炸雷就相当于新的排雷火箭
* 该点被引爆,所以从炸雷队列中排除
*/
if (R.count(point)) {
ans += C[point];
Q.push(make_pair(point, R[point]));
R.erase(point);
}
}
}
}
return ans;
}
int main() {
cin >> n>> m;
for (int i = 0; i < n; i++) {
int x, y, r;
cin >> x >> y >> r;
auto point = make_pair(x, y);
C[point] += 1;
R[point] = max(R[point], r);
}
cout << BFS() << endl;
return 0;
}
这题的思路是什么?
和我之前的思路有什么区别?为什么我超时了它没有?
因为爆炸范围很小,所以它遍历的是每次引爆范围内的所有点,检查这些点有没有雷(通过hashmap达到了O(1)
),而不是去遍历所有的雷,看会不会被引爆
检查范围内点的数量级是400
检查所有类遍历一次最大是5*104