第一步(前缀和)
计算并存储从i开始放钻石(一定放第i颗),最多连续放多少个
第二步(从末尾开始,算前缀和(后缀和))
比较并存储从i开始放钻石(不一定放第i颗)
第三步
先枚举(一个挨一个一个不落)左架子上放
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 100005
int dia[N];//存储输入钻石数的尺寸的数组
int left[N];//left[i]:左边架子从第i颗钻石开始放最多能放的个数(此时设定第i颗一定放到左边架子上)
int mx[N];//mx[i]从i及i以后的所有标号开始放钻石,最多能放几颗钻石
int main()
{int n = 0, k = 0, i = 1, l = 1, r = 1, ans = 0;cin >> n >> k;for (i = 1; i <= n; i++)cin >> dia[i];sort(dia + 1, dia + 1 + n);while (l <= n) {while (dia[r] - dia[l] <= k && r <= n)r++;//到第一个-dia[l]>k的地方停止r++::left[l] = r - l;l++;}for (i = n; i >= 1; i--)mx[i] = max(mx[i + 1], ::left[i]);//i=n时,max[i+1]=0,那么mx[n]=left[n]=1(考虑实际,第一个架子不可能从最后一个开始放,但第二个架子可以从最后一个开始放)//i从n开始的原因:// 利用已算出的left[i] 递推计算从i及i以后的所有标号开始,最多能放几颗钻石//隐含了比较从i(i从n->1)开始所有left[i]的大小这一过程//在递推的同时把从i开始所有left[i]最大值存储在了mx[i]中for (i = 1; i <= n; i++) {//肯定不能打断left[i](从第i颗钻石开始放时中断,不放到能放的最多)//原因:假设打断了左架子从i取钻石,即左架子没取到最多的钻石数就不放了// 想要从左架子中断处 开始继续让右架子计数// ->在左架子本来可到达的最长右端点处(dia[i+left[i]-1])之后继续计数,让右架子找的最大值// 1>要么和不打断左架子时相同,即(在左架子本应继续取钻石的地方让右架子计数 得到结果比不打断左架子时小)// 2>要么(假设在左架子本应继续取钻石的地方让右架子计数 得到结果比不打断左架子时大)// 左+右放钻石值=left[i]// 3>相等,归于上方两种情况之任一即可if (i + ::left[i] <= n) {ans = max(::left[i] + mx[i + ::left[i]], ans);//i = i+::left[i]-1;(-1是为了配合for循环+1的,本来可以不-1)此优化策略不可取,原因:mx[]从哪一位开始取未知,有可能到最后一组(i+::left[i]<=n但i+::left[i]*2>n时)错误跳过数据(即else里的部分情况)}else{ans = max(ans, ::left[i]);break;//有可能只在第一个架子上放就达到最大值,不用往右架子上放}cout << ans;return 0;
}
写::left[]是因为 编译器运行时报错:left[]不明确,猜测可能left可能是c++默认的命名空间之一查了一下发现
"std::left 是 C++ 标准库中的一个 I/O 操作符,用于将输出流中的内容左对齐"