leetcode 2576. 求出最多标记下标
题解:
这道题从左往右扫描和从右往左扫描都是错的。
比如 : 1 1 1 1 2 2 2 4 ,从右往左,如果将倒数的2 和 4 匹配了,那么剩下的都不能匹配了。但实际上全部都能匹配。
比如: 1 2 2 2 5 5 5 5,从左往右,如果将 1 和 2 匹配,两个 2 和 5 匹配,那么还有两个5不能匹配了。但实际上全部都能匹配。
最优方法应为将序列排序后分为两段,前半段和后半段分别从左往右依次配对。
如果可以匹配 k 对,那么也可以匹配小于 k 对,去掉一些数对即可做到。
如果无法匹配 k 对,那么也无法匹配大于 k 对(反证法)。
所以 k 越大,越无法选出 k 个能匹配的数对。有单调性,就可以二分答案。
class Solution {
public:
//结论:从小到大排序后,如果存在 k 对匹配,那么一定可以让最小的 k 个数与最大的 k 个数匹配。
//证明:假设不是最小的 k 个数与最大的 k 个数匹配,那么我们总是可以把 nums[i] 替换成比它小的且不在匹配中的数,这仍然是匹配的;
//同理,把 nums[j] 替换成比它大的且不在匹配中的数,这仍然是匹配的。所以如果存在 k 对匹配,那么一定可以让最小的 k 个数和最大的 k 个数匹配。
//反过来说,如果最小的 k 个数无法和最大的 k 个数匹配,则任意 k 对都无法匹配。(也可以用反证法证明)
//从小到大排序后,nums[0] 要与 nums[n−k] 匹配。如果不这样做,nums[0] 与在 nums[n−k] 右侧的数匹配,
//相当于占了一个位置,那么后续要选个更大的 nums[i] 与 nums[n−k] 匹配,这不一定能匹配得上。
//一般地,nums[i] 要与 nums[n−k+i] 匹配。如果对于所有的 0≤i<k,都满足 2⋅nums[i]≤nums[n−k+i],那么就可以从 nums 中选出 k 个能匹配的数对。
int maxNumOfMarkedIndices(vector<int>& nums) {
sort(nums.begin(),nums.end());
int size = nums.size(),i = 0;
for(int j = (size+1)/2;j < size;++j){//j = (size+1)/2相当于size为奇数时,不管正中间的数了
if(nums[i] * 2 <= nums[j]) ++i;
}
return i*2;
}
};