1. 程式人生 > >BZOJ2457 [BeiJing2011]雙端隊列 【貪心】

BZOJ2457 [BeiJing2011]雙端隊列 【貪心】

min 問題 BE ring fin main 區間 ace logs

題目

   Sherry現在碰到了一個棘手的問題,有N個整數需要排序。
   Sherry手頭能用的工具就是若幹個雙端隊列。
  

她需要依次處理這N個數,對於每個數,Sherry能做以下兩件事:
1.新建一個雙端隊列,並將當前數作為這個隊列中的唯一的數;
2.將當前數放入已有的隊列的頭之前或者尾之後。

對所有的數處理完成之後,Sherry將這些隊列排序後就可以得到一個非降的序列。

輸入格式

第一行包含一個整數N,表示整數的個數。接下來的N行每行包含一個整數Di,其中Di表示所需處理的整數。

輸出格式

其中只包含一行,為Sherry最少需要的雙端隊列數。

輸入樣例

6

3

6

0

9

6

3

輸出樣例

2

提示

100%的數據中N≤200000。

題解

由於最後排序的連續性,雙端隊列中元素的添加在最終排序中一定是連續的
所以我們在一個雙端隊列中加入一個元素後,那麽其僅可以向次小或次大方向擴展

我們將位置按數值大小排序,顯然每個低谷的地方是一段中最先出現的位置,這個位置先加入雙端隊列,然後就可以順序向兩端擴展

但是大小相同的元素可以互換位置

問題就轉化為了:可以移動一些區間內的元素位置,求波谷最小的序列

貪心取每個區間最大最小討論一下

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm> #define LL long long int #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); using namespace std; const int maxn = 200005,maxm = 100005
,INF = 0x7fffffff; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int A[maxn],id[maxn],n; inline bool cmp(const LL& a,const LL& b){return A[a] < A[b];} int main(){ n = read(); if (!n) {puts("0"); return 0;} for (int i = 1; i <= n; i++) A[i] = read(),id[i] = i; sort(id + 1,id + 1 + n,cmp); int way = 0,ans = 1,last = INF; for (int i = 1; i <= n; i++){ int r = i,mx = id[i],mn = id[i]; while (r < n && A[id[r + 1]] == A[id[r]]){ r++; mx = max(mx,id[r]); mn = min(mn,id[r]); } i = r; if (!way){ if (mx < last) last = mn; else last = mx,way = 1; }else { if (mn > last) last = mx; else last = mn,way = 0,ans++; } } printf("%d\n",ans); return 0; }

BZOJ2457 [BeiJing2011]雙端隊列 【貪心】