1. 程式人生 > 實用技巧 >【Codeforces 1370-D】Odd-Even Subsequence 二分+貪心

【Codeforces 1370-D】Odd-Even Subsequence 二分+貪心

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;
}