对于记录负半轴矿洞数量的数组 l,从索引 1 开始到 m,依次计算前缀和。即 li = li - 1 + li,这样 li 就表示数轴负半轴上从 -1 到 -i 位置的矿洞总数。通过前缀和,我们可以在后续计算中快速得到负半轴某一区间内的矿洞数量。
同理,对于记录正半轴矿洞数量的数组 r,也从索引 1 开始到 m,计算前缀和 ri = ri - 1 + ri,使得 ri 表示数轴正半轴上从 1 到 i 位置的矿洞总数。
(三)计算最大矿石获取量
1. 初始假设:
首先假设最大矿石获取量 ans 为只在正半轴或只在负半轴移动时能够挖掘到的最大矿石数。即 ans = max(rm, lm),这是一种简单的初步情况考虑,因为在某些情况下,仅在一个方向移动可能就能够获取最多的矿石。
2. 考虑混合移动策略:
通过循环遍历 i 从 1 到 m / 2(因为左右移动距离之和不能超过 m,所以 i 最大取到 m / 2),尝试不同的左右移动组合。 - 对于每一个 i 值,计算两种混合移动策略下能够挖掘到的矿石数:
先向右移动 i 单位距离,再向左移动 m - 2 * i 单位距离时,能够挖掘到的矿石数为 sr = ri + lm - 2 \* i。这里 ri 表示向右移动 i 单位距离过程中挖掘到的正半轴矿洞数,lm - 2 \* i 表示向左移动 m - 2 * i 单位距离过程中挖掘到的负半轴矿洞数。 - 先向左移动 i 单位距离,再向右移动 m - 2 * i 单位距离时,能够挖掘到的矿石数为 sl = li + rm - 2 \* i。这里 li 表示向左移动 i 单位距离过程中挖掘到的负半轴矿洞数,rm - 2 \* i 表示向右移动 m - 2 * i 单位距离过程中挖掘到的正半轴矿洞数。
每次计算出 sr 和 sl 后,更新最大矿石获取量 ans,即 ans = max(ans, sr, sl),取当前的 ans、sr 和 sl 中的最大值作为新的 ans。
3. 加上起始点矿洞数量:
最后,将起始点(坐标为 0)的矿洞数量 s 加到 ans 中,因为这些矿洞在初始位置就可获取,无需移动。此时得到的 ans 即为在移动距离不超过 m 的前提下,小蓝最多能获得的矿石单位数量。 通过以上步骤,我们可以全面且高效地解决在给定条件下的挖矿问题,找到获取最多矿石的方案。
#include <bits/stdc++.h>
// 定义 solve 函数来解决挖矿问题
void solve()
{
int n, m;
// 从标准输入读取矿洞数量 n 和最大移动距离 m
std::cin >> n >> m;
// s 用于记录坐标为 0 的矿洞数量
int s = 0;
// 定义两个静态数组 l 和 r 来分别记录负半轴和正半轴矿洞的前缀和
// 数组大小为 m + 1,初始化为 0
int l[10000001] = {0};
int r[10000001] = {0};
// 遍历每个矿洞
for (int i = 0; i < n; ++i)
{
int x;
// 读取当前矿洞的坐标
std::cin >> x;
// 如果矿洞坐标的绝对值小于等于最大移动距离 m 且为负数
//abs为绝对值函数
if (std::abs(x) <= m && x < 0)
{
// 对应负半轴的位置计数加 1
l[-x]++;
}
// 如果矿洞坐标的绝对值小于等于最大移动距离 m 且为正数
else if (std::abs(x) <= m && x > 0)
{
// 对应正半轴的位置计数加 1
r[x]++;
}
// 如果矿洞坐标为 0
else if (x == 0)
{
// 坐标为 0 的矿洞数量加 1
s++;
}
}
// 计算负半轴矿洞的前缀和
for (int i = 1; i <= m; ++i)
{
l[i] += l[i - 1];
}
// 计算正半轴矿洞的前缀和
for (int i = 1; i <= m; ++i)
{
r[i] += r[i - 1];
}
// 先假设最大矿石数为只走正半轴或只走负半轴能挖到的最大矿石数
int ans = std::max(r[m], l[m]);
// 尝试不同的左右移动组合,i 表示先向一个方向移动的距离
for (int i = 1; i <= m / 2; ++i)
{
// 先向右移动 i 的距离,再向左移动 m - i * 2 的距离能挖到的矿石数
int sr = r[i] + l[m - i * 2];
// 先向左移动 i 的距离,再向右移动 m - i * 2 的距离能挖到的矿石数
int sl = l[i] + r[m - i * 2];
// 更新最大矿石数
ans = std::max({ans, sr, sl});
}
// 加上坐标为 0 的矿洞数量
ans += s;
// 输出最大能获得的矿石数
std::cout << ans << '\n';
}
int main()
{
solve();
return 0;
}
2.C语言代码
cpp复制代码
#include <stdio.h>
#include <math.h>
#define MAX_M 10000001
// 定义 solve 函数来解决挖矿问题
void solve() {
int n, m;
// 从标准输入读取矿洞数量 n 和最大移动距离 m
scanf("%d %d", &n, &m);
// s 用于记录坐标为 0 的矿洞数量
int s = 0;
// 定义两个静态数组 l 和 r 来分别记录负半轴和正半轴矿洞的前缀和
// 数组大小为 MAX_M,初始化为 0
int l[MAX_M] = {0};
int r[MAX_M] = {0};
// 遍历每个矿洞
for (int i = 0; i < n; ++i) {
int x;
// 读取当前矿洞的坐标
scanf("%d", &x);
// 如果矿洞坐标的绝对值小于等于最大移动距离 m 且为负数
if (abs(x) <= m && x < 0) {
// 对应负半轴的位置计数加 1
l[-x]++;
}
// 如果矿洞坐标的绝对值小于等于最大移动距离 m 且为正数
else if (abs(x) <= m && x > 0) {
// 对应正半轴的位置计数加 1
r[x]++;
}
// 如果矿洞坐标为 0
else if (x == 0) {
// 坐标为 0 的矿洞数量加 1
s++;
}
}
// 计算负半轴矿洞的前缀和
for (int i = 1; i <= m; ++i) {
l[i] += l[i - 1];
}
// 计算正半轴矿洞的前缀和
for (int i = 1; i <= m; ++i) {
r[i] += r[i - 1];
}
// 先假设最大矿石数为只走正半轴或只走负半轴能挖到的最大矿石数
int ans = (r[m] > l[m]) ? r[m] : l[m];
// 尝试不同的左右移动组合,i 表示先向一个方向移动的距离
for (int i = 1; i <= m / 2; ++i) {
// 先向右移动 i 的距离,再向左移动 m - i * 2 的距离能挖到的矿石数
int sr = r[i] + l[m - i * 2];
// 先向左移动 i 的距离,再向右移动 m - i * 2 的距离能挖到的矿石数
int sl = l[i] + r[m - i * 2];
// 更新最大矿石数
if (sr > ans) ans = sr;
if (sl > ans) ans = sl;
}
// 加上坐标为 0 的矿洞数量
ans += s;
// 输出最大能获得的矿石数
printf("%d\n", ans);
}
int main() {
solve();
return 0;
}