【蓝桥杯 2024 省 C】挖矿
蓝桥杯专栏:2024 省 C
算法竞赛:技巧,前缀和,双指针,贪心
题目链接:洛谷【蓝桥杯 2024 省 C】挖矿
题目描述:
小蓝正在数轴上挖矿,数轴上一共有 n n n 个矿洞,第 i i i 个矿洞的坐标为 a i a_i ai。小蓝从 0 0 0 出发,每次可以向左或向右移动 1 1 1 的距离,当路过一个矿洞时,就会进行挖矿作业,获得 1 1 1 单位矿石,但一个矿洞不能被多次挖掘。小蓝想知道在
移动距离不超过 m m m 的前提下,最多能获得多少单位矿石?
输入格式:
输入的第一行包含两个正整数 n , m n,m n,m,用一个空格分隔。
第二行包含 n n n 个整数 a 1 , a 2 , ⋯ , a n a_1, a_2,\cdots, a_n a1,a2,⋯,an,相邻整数之间使用一个空格分隔。
输出格式:
输出一行包含一个整数表示答案。
数据范围:
对于 20 % 20\% 20% 的评测用例, 1 ≤ n ≤ 10 3 1 \le n \le 10^3 1≤n≤103;
对于所有评测用例, 1 ≤ n ≤ 10 5 1 \le n \le 10^5 1≤n≤105, − 10 6 ≤ a i ≤ 10 6 -10^6 \le a_i \le 10^6 −106≤ai≤106, 1 ≤ m ≤ 2 × 10 6 1 \le m \le 2 \times 10^6 1≤m≤2×106。
题目大意
小蓝初始时在原点处,他可以在一维数轴上左右行走,数轴上某个位置处有一个或多个矿洞,每路过一个矿洞就会获得 1 1 1 单位矿石,求在移动距离不超过 m m m 的前提下,最多能获得多少单位的矿石。
题目分析
初看有点像区间 DP 问题,然而这比区间 DP 问题简单得多,因为这个问题只有路程这一个限制条件,根据贪心,我们很容易知道,必定存在某种能达到矿石数量最多的方案是先沿某个方向走一定距离然后停止移动或返回原点再向另一个方向走一定距离。
那我们就可以在走过 1 ∼ m 1\sim m 1∼m 的每个距离时让小蓝掉头向另一个方向走,这样可以在 O ( n ) \mathcal{O}(n) O(n) 的时间复杂度内将问题求解出来。
为了确保能在 O ( 1 ) \mathcal{O}(1) O(1) 的时间复杂度内知道在 1 ∼ i 1\sim i 1∼i 或 − i ∼ − 1 -i\sim -1 −i∼−1 上有多少个矿洞,我们可以使用前缀和的方法预处理出来在两个方向上的矿洞数量前缀和。
我们令左指针 l l l 在原点或负半轴上,右指针 r r r 在原点或正半轴上,根据上文所述在某个方向走的距离发生改变,另一个方向上能走的距离也必然发生改变,则可以枚举指针变化情况求解,这里用到了双指针的思想,但具体实现可以不将双指针体现出来。
题目实现
根据上述分析,程序过程如下:
- 读入矿洞坐标,在正半轴和负半轴的分别用两个数组记录,在原点的用变量计数即可;
- 预处理两个方向上的矿洞数量的前缀和;
- 枚举距离 1 ∼ m 1\sim m 1∼m,分别求先向负半轴再向正半轴走和先向正半轴再向负半轴走两种情况,每个距离,每种情况取最大值,最终结果还要加上在原点的矿洞。
AC Code
参考代码(C++):
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
int a[N],b[N],ans,num;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
if (x>0) a[x]++;
else if (x<0) b[-x]++;
else num++;
}
for (int i=1;i<=m;i++)
a[i]+=a[i-1],b[i]+=b[i-1];
for (int i=1;i<=m;i++)
{
int cnt=a[i];
if (m-i*2>0) cnt+=b[m-i*2];
ans=max(ans,cnt);
cnt=b[i];
if (m-2*i>0) cnt+=a[m-i*2];
ans=max(ans,cnt);
}
printf("%d",ans+num);
return 0;
}
End
感谢观看,如有问题欢迎指出。
更新日志
- 2025/8/24 开始书写本篇 CSDN 博客,并完稿发布。