2344: 盖伦的告白
时间限制: 1 Sec 内存限制: 128 MB提交: 56 解决: 19
[ 提交][ 状态][ 讨论版]
题目描述
盖伦和赵信这对基友又在打赌,谁输了就去向卡特琳娜告白。。
这一季中盖伦一如既往的怂。赵信为了帮他一把,故意表现出很厉害的样子,结果成功激活了盖伦的智商。
赵信手中有n张牌,每张牌上面有一个数,这些牌是非递减的。现在盖伦从他手中抽取k张牌,如果盖伦抽取后剩下的牌中相邻两张的差值的最大值最小,就算他赢。
结果当然是盖伦赢了。那么问题来了!
输入
第一行两个正整数n和k。
第二行n个数代表n张牌。
3≤n≤105
1≤k≤n-2
-109≤Ai≤109
输出
输出只有一个数,上述中的最小值。
样例输入
5 1
1 2 4 7 8
8 2
1 2 3 5 8 13 17 18
5 1
1 2 4 6 9
样例输出
3
5
2
链接:传送门
由于题目中给出 这些数字是非递减的,那么从中间删,肯定会产生更大差值所以要考虑从两边删
所以思路就是从两边删除k个元素,取剩下n-k个元素的最值即可,有两种写法
1:线段树O(nlogn)
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=100005;
ll a[N],t[N<<2];
void build(ll l,ll r,ll rt)
{
if(l==r)
{
t[rt]=a[l+1]-a[l];
return;
}
ll m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
ll query(ll x,ll y,ll l,ll r,ll rt)
{
if(l>=x&&r<=y)
{
return t[rt];
}
ll m=(l+r)>>1;
if(y<=m)
return query(x,y,l,m,rt<<1);
else if(x>m)
return query(x,y,m+1,r,rt<<1|1);
else
return max(query(x,m,l,m,rt<<1),query(m+1,y,m+1,r,rt<<1|1));
}
int main()
{
ll n,k;
while(~scanf("%lld%lld",&n,&k))
{
mem(t,0);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
ll len=n-k-1,ans=0x3f3f3f3f;
build(1,n-1,1);
for(ll i=1;i+len<=n;i++)
ans=min(ans,query(i,len+i-1,1,n-1,1));
printf("%lld\n",ans);
}
}
2.双端队列 O(n)
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=100010;
int a[N],diff[N],q[N],idx[N];
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
if(i>1)diff[i-1]=a[i]-a[i-1];
}
int head=0,rear=0;
for(int i=1;i<=n-k-1;i++)
{
while(head<rear&&q[rear-1]<=diff[i])
rear--;
q[rear]=diff[i];
idx[rear++]=i;
}
int ans=q[head];
for(int i=n-k;i<=n-1;i++)
{
while(head<rear&&q[rear-1]<=diff[i])
rear--;
q[rear]=diff[i];
idx[rear++]=i;
while(head<rear&&idx[head]<=i-n+k+1)head++;
ans=min(ans,q[head]);
}
printf("%d\n",ans);
}
return 0;
}