樹狀數組求最長上升子序列


先說最簡單的做法:

一種是最常見的dp方法,令f[i]表示以A[i]元素結尾的LIS長度,那么,F[i]=max{F[j]+1) 其中1<=j<i,A[j]<A[i],邊界是初始化F[i]=1,復雜度O(n^2)。

#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)

int n, a[1005], f[1005];
int main () {
  scanf("%d", &n);
  FOR(i, 1, n) scanf("%d", &a[i]), f[i] = 1;
  FOR(i, 1, n)
    FOR(j, 1, i - 1)
      if (a[j] < a[i])
        f[i] = max(f[i], f[j] + 1);
  int ans = 0;
  FOR(i, 1, n)
    ans = max(ans, f[i]);
  printf("%d\n", ans);
  return 0;
}

另一種是有些貪心思想,利用二分:

如果子序列長度相同,那么最末尾的元素越小,就越有優勢,於是對於長度相同的子序列,我們總是用更小的來替換。

#include <cstdio>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int a[1005], n;

int main() {
    for (int i = 0; i < 1005; ++i)
        a[i] = inf;
    scanf("%d", &n);
    for (int i = 0, t; i < 1005; ++i) {
        scanf("%d", &t);
        *lower_bound(a, a + n, t) = t;
    }
    printf("%d\n", lower_bound(a, a + n, inf) - a);
    return 0;
}

 

另一種,用樹狀數組,這種方法和第一種方法思路是一致的,只不過在尋找比自己小的元素中,最長的那個LIS的過程,利用了樹狀數組來加速。之前的思想是:當前元素的LIS是位置在自己之前的,且LIS是最大的那個值+1。先不論樹狀數組是怎么工作的,對於原始數組v,復制一個數組a來排序,之后遍歷數組v。這里由於是按順序遍歷v的,所以對於v[i],先操作的元素肯定位置上在v[i]之前。

然后,對於v[i],得到其在a中的位置p,想辦法得到在p之前的LIS長度是多少(就是操作query(p-1)),假設是q,那么ans=max(ans, q+1)。這里,由於對於v[i]實際操作的是p,p是v[i]值大小的相對位置,於是又滿足:在位置p前面的那些元素,必然大小是<v[i]的,綜上,的確可以同意:這和之前LIS解法一的思想是一致的了。

現在的要講樹狀數組怎么工作,即如何完成上面的”想辦法“,無非就是要完成查詢位置p之前的最大值(區間最大值),以及對於當前元素v[i]和其在a中位置的p,得到LIS后更新位置p之前的LIS。以往BIT總是用來求區間和,其實這只是它的一個應用,說到底它是一個數據結構,方法就是將sum和add的加法操作變成最值操作。  理解到這兒:其實,這完全可以用線段數來實現,只不過這里要處理的區間恰好是[1, p],即恰好一定是從1開始的,用樹狀數據能簡單解決,就不必用線段數了。

#include <cstdio>
#include <algorithm>
using namespace std;

#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 10005
int n, L, v[maxN], a[maxN], T[maxN];

void update(int i, int x) { 
  for (; i <= L; i += i & -i)
    T[i] = max(T[i], x);
}
int query(int i) {
  int ans = 0;
  for (; i; i -= i & -i)
    ans = max(ans, T[i]);
  return ans;
}
int main () {
  scanf("%d", &n);
  FOR(i, 0, n - 1) scanf("%d", &v[i]), a[i] = v[i];
  sort(a, a + n);
  L = unique(a, a + n) - a;
  int ans = 1, t;
  FOR(i, 0, n - 1) {
    int p = lower_bound(a, a + L, v[i]) - a + 1;
    t = query(p - 1) + 1;
    ans = max(ans, t);
    update(p, t);
  }
  printf("%d\n", ans);
  return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM