题目链接
https://nanti.jisuanke.com/t/38
解题思路
该题AC有两种思路
二分查找
- 将光源的横坐标排序
- 对于每个种子(横坐标 x ), 找到距离他最近的大于等于其坐标光源 light_right
- 找到距离他最近的小于其坐标的光源 light_left
- 比较得到最短光源距离,从而得到每个种子的高度
代码 ( C )
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int light[100001];
int n, m, h;
int min(int a, int b) {
return (a < b) ? a : b;
}
int max(int a, int b) {
return (a > b) ? a : b;
}
int abs(int a) {
return (a < 0) ? -a : a;
}
// 二分查找, 找到距离i最近的大于等于i的值
int find(int i) {
int from = 1;
int to = m;
while (from != to) {
int mid = (from + to) / 2;
if (light[mid] == i) return mid;
if (light[mid] > i) to = max(mid - 1, from);
else from = mid + 1;
}
return light[from] > i ? from : from + 1;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d %d %d", &n, &m, &h);
memset(light, 0, sizeof(light));
for (int i = 1; i <= m; i++) {
scanf("%d", light+i);
}
// 将光源位置排序
qsort(light+1, m, sizeof(int), compare);
for (int i = 1; i <= n; i++) {
// 如果没有光源,直接输出0
if (!m) {
printf("0\n");
continue;
}
int light_right = find(i);
int rightDistance = abs(light[light_right] - i);
int leftDistance = (right > 1) ? abs(light[right-1] - i) : h;
printf("%d\n", max(h - min(rightDistance, leftDistance), 0));
}
}
}
双指针扫描
很巧的一种方法, 复杂度为 O(n)
代码( C )
#include <stdio.h>
#include <string.h>
#define MAX_LEN 100003
int left[MAX_LEN];
int right[MAX_LEN];
int n, m, h;
int max(int a, int b) {
return (a > b) ? a : b;
}
int min(int a, int b) {
return (a < b) ? a : b;
}
void scan() {
int len = max(m, n);
int light_l = -h;
int light_r = len + h;
for (int i = 1; i <= len; i++) {
if (left[i] != -1) light_l = left[i];
else left[i] = light_l;
if (right[len - i + 1] != -1) light_r = right[len - i + 1];
else right[len - i + 1] = light_r;
}
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d %d %d", &n, &m, &h);
memset(left, 0xff, sizeof(left));
memset(right, 0xff, sizeof(right));
for (int i = 0; i < m; i++) {
int light;
scanf("%d", &light);
left[light] = right[light] = light;
}
scan();
for (int i = 1; i <= n; i++) {
int d = h;
if (left[i] != -1) d = min(d, i - left[i]);
if (right[i] != -1) d = min(d, right[i] - i);
printf("%d\n", max(h - d, 0));
}
}
}
总结
一开始我也想到了二分查找的思想,但是之前教科书上的二分查找的代码都是针对数组中存在要找的元素的,而本题中则需要查找距离其最近的元素(C++可以用lower_bound(), 但是我之前不知道),所以find()函数一直没写对.
双指针扫描的方法我是在计蒜客的讨论区中找到的,看懂了思想但是原版代码不是AC,后来发现问题出现在scan()里面,扫描的范围应该是max(m, n)而不是m或者n.
总之这道题个人认为比较经典,看起来不难但是有小细节要仔细扣扣. 很喜欢.