Description
给你一个N 个数组成的序列V,要你删除其中K 个数,M 表示剩下的数字
中任意两个数的差值的最大值,m 表示最小差值,要你计算删除K 个数后,M+m
的最小值。
Input
第一行包含两个整数N(3<=N<=1,000,000)和K(1<=K<=N-2);
第二行包含N 个空格隔开的数, 表示给定的序列
V(-5,000,000<=Vi<=5,000,000)。
Output
输出M+m 的最小值。
Sample Input
输入1:
5 2
-3 -2 3 8 6
输入2:
6 2
-5 8 10 1 13 -1
输入3:
6 3
10 2 8 17 2 17
Sample Output
输出1:
7
输出2:
13
输出3:
6
Data Constraint
100%的数据 3<=N<=1,000,000,1<=K<=N-2),-5,000,000<=Vi<=5,000,000
Solution
题目大意就是让你求在一个序列中删除k个数,让你最小化这n-k个数的任意两个数的最大差值和最小差值之和。
一个贪心的策略就是我们肯定是先删除最大或最小的数,证明很简单,若我们删掉的不是最大或最小的数,那么最大差值不会变,任然是最大的数减去最小的数,但是最小的差值有可能增加,所以我们肯定不能每次删除除最大最小值之外的数。换言之,问题转化为:在一个n个数的序列中从前面和后面一共删除k个数,最小化这n-k个数的最大差值和最小差值之和。
一个暴力的方法就是首先将这n个数排序后,枚举前面删多少个数(i),那么后面就删了k-i个数,然后我们知道最大差值即为两边数之差,但最小值只能再O(n)求一次。时间复杂度O(n^2)。
考虑若何优化最小值,一个方法是用线段树,但是会超空间,一个可行的方案即为用单调队列,我们发现当因为我们的区间是不断向右移动的(形象理解一下),所以当有一个差值加进队列里时,我们发现比它大的都一定不是最小值,所以都可以删掉,这样我们就可以维护一个单调递增的序列,每次将队头的元素更新ans即可。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000010
using namespace std;
int n,k,a[N],mi[N],q[N],l,r,ans=1<<30;
int read(){
int w=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*w;
}
int main(){
freopen("delete.in","r",stdin);
freopen("delete.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n);
for(int i=1;i<=n-1;i++) mi[i]=a[i+1]-a[i];
l=1;r=0;
for(int i=1;i<=n;i++){
while(l<=r&&q[l]<=i-n+k) l++;
if(i>=n-k){
ans=min(ans,a[i]-a[i-n+k+1]+mi[q[l]]);
}
while(l<=r&&mi[i]<mi[q[r]]) r--;
q[++r]=i;
}
printf("%d\n",ans);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/87893972