[USACO16OPEN] Diamond Collector S | (贪心+线性DP)

Posted zhwer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO16OPEN] Diamond Collector S | (贪心+线性DP)相关的知识,希望对你有一定的参考价值。

题目描述

Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected (N) diamonds ((N leq 50,000)) of varying sizes, and she wants to arrange some of them in a pair of display cases in the barn.

Since Bessie wants the diamonds in each of the two cases to be relatively similar in size, she decides that she will not include two diamonds in the same case if their sizes differ by more than (K) (two diamonds can be displayed together in the same case if their sizes differ by exactly (K)). Given (K), please help Bessie determine the maximum number of diamonds she can display in both cases together.

奶牛 Bessie 很喜欢闪亮亮的东西(Baling~Baling~),所以她喜欢在她的空余时间开采钻石!她现在已经收集了 (N) 颗不同大小的钻石 ((N<=50,000)) ,现在她想在谷仓的两个陈列架上摆放一些钻石。

Bessie 想让这些陈列架上的钻石保持相似的大小,所以她不会把两个大小相差 (K) 以上的钻石同时放在一个陈列架上(如果两颗钻石的大小差值为K,那么它们可以同时放在一个陈列架上)。

现在给出 (K) ,请你帮 Bessie 确定她最多一共可以放多少颗钻石在这两个陈列架上。

输入格式

The first line of the input file contains (N) and (K) ((0 leq K leq 1,000,000,000)) .

The next (N) lines each contain an integer giving the size of one of the

diamonds. All sizes will be positive and will not exceed (1,000,000,000).

输出格式

Output a single positive integer, telling the maximum number of diamonds that

Bessie can showcase in total in both the cases.

输入输出样例

输入 #1

7 3
10
5
1
12
9
5
14

输出 #1

5

———————————————————————————

由于大小相差不超过 (K) 的钻石才能放在同一个陈列架上,所以我们在把钻石大小排序之后同一陈列架的钻石一定是一段连续区间,区间长度只取决于最小值和最大值(差值不大于 (K)

如果有两个陈列架的话,根据贪心思想,两者应该是两端不相邻的连续区间(如果相邻的话可以合并成一段)

接下来的问题就变成了用什么策略确定这两段区间,首先将序列从小到大排序

最初的想法是利用尺取法贪心地找到第一段最长区间,第二次忽略掉最长区间找次长区间

但由于最长区间可以有很多个,忽略的操作显然对找次长区间有后效性,所以这是一个伪算法

正解(之一)的思路如下:

既然是两个互不相交的序列,那么也就是说选取一个断开点,让该断开点左边的数形成一个序列,右边的数形成另外一个序列。

就可以用一种DP的思想,用 (l[i]) 记录 (i) 左边的最长的可延伸长度(不一定和第 (i) 个数有关),(r[i]) 同理

这样就可以枚举每一个断开点,求它左边的最长长度加上右边的最长长度的和的最大值

如果是朴素一个个地延伸,那么理论时间复杂度是 (O(n^2)) 的(当然实际上远跑不满),会 TLE

根据序列的单调性,用两个二分函数 (toLeft())(toRight()) 向左/右寻找第一个大于 i 号钻石的大小值,与 (i) 作差即为可能的序列长度,这样做时间复杂度是 (O(nlogn))

但是要注意左右两端都选了 (i) 的情况,下面代码中用 (at[]) 防止计数多了

代码如下

#include <bits/stdc++.h>
#define MAXN 50007
using namespace std;
int n,k,ans,diam[MAXN];
int l[MAXN],r[MAXN],at[MAXN];
inline int toRight(int left,int right,int val) {
	int ret=n+1,mid;
	while (left<=right) {
		mid=left+(right-left)/2;
		if (diam[mid]>val+k) ret=mid,right=mid-1;
		else left=mid+1;
	}
	return ret;
}
inline int toLeft(int left,int right,int val) {
	int ret=0,mid;
	while (left<=right) {
		mid=left+(right-left)/2;
		if (diam[mid]<val-k) ret=mid,left=mid+1;
		else right=mid-1;
	}
	return ret;
}
int main() {
	memset(l,0,sizeof(l));
	memset(r,0,sizeof(r));
	memset(at,0,sizeof(at)); 
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&diam[i]);
	sort(diam+1,diam+n+1);
	for (int i=1;i<=n;i++) {
		if (i-toLeft(1,i-1,diam[i])>l[i-1])
			l[i]=i-toLeft(1,i-1,diam[i]),at[i]++;
		else l[i]=l[i-1];
	}
	for (int i=n;i>=1;i--) {
		if (toRight(i+1,n,diam[i])-i>r[i+1])
			r[i]=toRight(i+1,n,diam[i])-i,at[i]++;		
		else r[i]=r[i+1]; 
	}
	for (int i=1;i<=n;i++)
		ans=max(ans,l[i]+r[i]+(at[i]==2?-1:0));
	printf("%d",ans);
	return 0;
}







以上是关于[USACO16OPEN] Diamond Collector S | (贪心+线性DP)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector

P3143 [USACO16OPEN]钻石收藏家Diamond Collector(伸缩法)

P3143 [USACO16OPEN]钻石收藏家Diamond Collector[two-pointers]

Luogu P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解

Bzoj 4582 [Usaco2016 Open] Diamond Collector 题解

BZOJ 4582 [Usaco2016 Open]Diamond Collector