POJ 1631 - Bridging signals ( LIS )

題意

Bridging signals
有p條線路,它們有可能相交。現在讓你去掉一些線路,使得剩下的線不相交且線最多 ( p < 40000 )

思路

爲使得連線不相交, 必須選擇一個上升的連接序列, 而求剩下的線不相交且最多就是求LIS ( 最長上升子序列 )
由於p最大達40000, 1000 ms 肯定不能用複雜度爲O(n*n)的樸素動態規劃,會超時。這時考慮用O(nlogn)二分+貪心

建一個dp數組,dp[i]表示長度爲i的LIS結尾元素的最小值。貪心策略:對於一個上升子序列,顯然其結尾元素越小,越有利於在後面接其他的元素,也就越可能變得更長。因此,我們只需要維護dp數組,對於每一個a[i],如果a[i] > len[當前最長的LIS長度],就把a[i]接到當前最長的LIS後面,即dp[++當前最長的LIS長度]=a[i]。


關於維護dp數組 :
對於每一個a[i],如果a[i]能接到LIS後面,就接上去;否則,就用a[i]取更新dp數組。具體方法是,在dp數組中找到第一個大於等於a[i]的元素dp[j],用a[i]去更新dp[j]。如果從頭到尾掃一遍dp數組的話,時間複雜度仍是O(n^2)。我們注意到dp數組內部一定是單調不降的,所有我們可以二分dp數組,找出第一個大於等於a[i]的元素。二分一次dp數組的時間複雜度的O(logn),所以總的時間複雜度是O(nlogn)。

目前爲止看到過的最易理解的LIS – O(nlogn)算法分析:
最長上升子序列(LIS)長度的O(nlogn)算法

AC代碼

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
const int maxn = 40000+5;
const int INF = 0x3f3f3f3f;
int dp[maxn], a[maxn];

int main()
{
    int T, n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for( int i = 0; i < n; i++ )
            scanf("%d",&a[i]);
        int len = 1;
        for( int i = 0; i < n; i++ ){
            int p = lower_bound(dp, dp+len, a[i]) - dp;
            dp[p] = a[i];
            if( p == len ) len++;
        }
        printf("%d\n",len-1);
    }
    return 0;
}
發佈了150 篇原創文章 · 獲贊 13 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章