📅  最后修改于: 2023-12-03 15:25:50.291000             🧑  作者: Mango
给定一个长度为n的未排序数组a,并定义数组变换操作Sw(a,i,j)为将a数组中下标为i和j的元素交换位置,现在对于数组a,我们可以执行任意多次Sw操作,我们希望找到一种操作方案,使得执行完所有操作后,a数组的最小值最大。
请你编写一个函数,输入一维数组a,输出执行操作后的最小值最大值。
首先可以看出本题有两种方式可以做到最优解,即二分查找和贪心算法。
因为本题是要找到最小值的最大值,所以需要在一个区间内二分查找最大值。
在判断一个数是否为正解时,可以使用贪心的思路:尽量现在选择排在前面的元素,直到无法交换,再选择排在后面的元素。
具体实现时,可以把所有数初始排序,然后设置low和high两个指针,将low指向最小元素,将high指向最大元素,然后对区间进行二分查找,对于每个mid,都执行一遍上述的贪心算法,直到找到正解或low>high为止。
贪心算法的思路如上所述,先对数组进行排序,再用low和high指向最小元素和最大元素,这时候可以得到一个初步的正解。接着,从元素a[low]开始向后扫描,若a[i]>a[low],则一定可以通过Sw操作将前面的元素和a[i]交换,这样可以得到较小的a[low]和较大的a[i],否则a[low]就不可能再被更改,low指针再向后移动一位。
依此类推,对于所有i>low,如果a[i]>a[low],就尝试进行Sw操作,如果能操作成功,就将low指针后移,否则i指针后移。最终可以得到最小值的最大值。
def min_max(a: List[int]) -> int:
n = len(a)
a.sort()
low, high = 0, n-1
while low < high:
mid = (low + high + 1) // 2
if check(a, n, mid):
low = mid
else:
high = mid - 1
return a[low]
def check(a: List[int], n: int, k: int) -> bool:
cnt = 0
i, j = 0, 1
while i < n and j < n:
while j < n and a[j] - a[i] <= k:
j += 1
cnt += j - i - 1
i += 1
return cnt >= n * (n-1) // 4
#include<bits/stdc++.h>
using namespace std;
const int INF=2147483647,N=100010;
int n,a[N],tmax=-INF;
bool check(int x){
int lw=1,hi=1,add=0,cnt=0;
if(x<tmax-a[1]) return false;
for(int i=1;i<n;i++){
while(hi<n && a[hi+1]<=x+a[i]) hi++;
cnt+=(hi-lw+add);//2.计算小于等于x的数的个数
add+=(hi-lw);//3.计算等于x的数的个数
while(a[i+1]-a[lw]>x) {
add-=(hi-lw);
lw++;//4.计算小于等于a[i]的数的个数
}
}
return cnt>=(n-1)*n/4;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],tmax=max(tmax,a[i]);
sort(a+1,a+1+n);
int l=0,r=tmax-a[1]+1,ans;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) l=mid+1,ans=mid;//1.使用二分查找
else r=mid-1;
}
cout<<ans;
return 0;
}