题意
给出一个数组 a ,让你选择一个 a 的子序列,使得 (min(max(a_1,a_3,a_5...),max(a_2,a_4,a_6...))) 最小。
即奇数位置的最大值和偶数位置的最大值 的最小值最小。
思路
其实做这道题我还是很懵逼的,因为如果从选数的角度考虑的话,该选那些数字?这个问题不好解决。
后来我想了枚举每个值作为答案的时候,是否合法,这样复杂度比较高,这时就想到了二分。
把原数组排序,设 l = 1,r = n,二分结果分别从奇数,偶数中诞生,为 arr[mid] 是否合法。
怎么检验合法呢?
假如当前二分的是奇数作为答案,首先我们从头开始选,如果这个值小于等于 x (当前二分的答案值),那么把它放到当前选择的奇数当中,这时下一个能选择的距离这个值的距离要大于 1 。就这样选择下去,最后看剩余的数字最大值是不是大于等于 x。
我写完之后突然意识到我没有判断 x 有没有被选择到奇数队列中,因为我们是把 x 作为奇数的最大值,那就要保证 x 要在奇数中,这样一来,竟然不知道该如何检验合法性。便放弃了二分的想法。(其实一直二分下去就对了,不在,说明这个 x 不是最佳答案,我没考虑好)
后想了一个贪心的想法,假如答案在奇数产生,我们只需要保证奇数的所有值尽可能的小。
按照他们的大小把下标进行排序,一次进行选择,如果当前数字的邻居并没有被选择,那么当前数字可以选择。
最后根据判断奇数和偶数产生的答案,输出较小值。Wa6了。
看了一下之前是因为代码写错了wa6了,最后wa9 是因为这样找可能根本找不到满足条件的序列。
题解
直接二分答案,l = 0,r = 1e9 。
这样不用考虑当前二分的 x 在不在当前队列中,不在说明 x 不是最小值 ,继续二分就可以了。
检验的时候检验是否可以找到一个长度为 k 的队列。
看代码理解
#include<bits/stdc++.h>
#define pb push_back
const int N=1e6+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int arr[N],n,k;
int check(int x,int m)
{
int num=0;
for(int i=1; i<=n; i++)
{
if(arr[i]<=x||num%2==m)
num++;
}
return num>=k;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
scanf("%d",&arr[i]);
int l=0,r=1e9,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid,0)||check(mid,1))
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
printf("%d
",ans);
return 0;
}