考点:前缀和 + 枚举
时间复杂度:O(n)
分析
简单手玩一下分析可以得到:要么一直朝着一个方向,要么折返一次,因为题目明确说了一个矿洞不能被多次挖掘,多走并不能提供贡献,考虑枚举走i步分析这两种大情况,每种大情况又对应着左右的小情况,都去更新一下最佳贡献即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 21;
int n, m, cnt0, x, ans;
// l[N] 存储负数位置的元素统计信息,而 r[N] 存储正数位置的元素统计信息
int l[N], r[N];
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> x;
if (x < 0) l[-x] ++;
else if(x > 0) r[x] ++;
else cnt0 ++;
}
// 求左右前缀和的作用
// 用于统计在不同步数下,向左或者向右移动能够到达的元素数量,从而计算出最大的可达元素数量
for (int i = 1; i <= m; i++) l[i] += l[i - 1], r[i] += r[i - 1];
// 枚举走的步数i
for (int i = 1; i <= m; i++) {
// 一直往左走
ans = max(ans, l[i]);
// 一直往右走
ans = max(ans, r[i]);
// 往左走i步向右折返,右边的有效距离实际上只有m - i * 2,返回走的i没有贡献,下面同理
if (m - i * 2 > 0) ans = max(ans, l[i] + r[m - i * 2]);
// 往右走i步向左折返
if (m - i * 2 > 0) ans = max(ans, r[i] + l[m - i * 2]);
}
// 这一步是显然的,0 的个数都应该被加上
cout << ans + cnt0 << "\n";
return 0;
}