【Codeforces 1370-D】Odd-Even Subsequence 二分+貪心
題意
給出一個數組 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\n",ans); return 0; }