1. 程式人生 > >uva1471

uva1471

ans ng- printf 解法 pac 最大 post track ios

這是LIS的變形,題意是求一個序列中去掉某個連續的序列後,能得到的最長連續遞增序列的長度。

  用DP的解法是:吧這個序列用數組a來記錄,再分別用兩個數組f記錄以i結尾的最長連續遞增序列的長度,g[i]記錄以i開頭的最長連續遞增序列。

然後像求DP求LIS一樣遍歷整個序列求出i前面所有小於a[i]的元素中以該元素結尾的最長序列f[j], 那麽 dp[i] = g[j] + f[i],?這樣時間復雜度為O(n^2)。

  因為和普通的LIS相似,所以能夠利用LIS的優化方法把該題的時間復雜的優化到O(nlogn)。方法仍是利用一個數組d[i]記錄長度為 i 的連續遞增序列的最後一個元素的最小值,顯然該序列是單調遞增的。所以上面紅色字體的操作能夠通過二分查找直接得到f[j]的值,進而得到一個可行的長度ans, 然後更新數組d就可以,更新的方法是假設以a[i]小於數組d中記錄的與a[i]長度同樣的序列的最後一個元素的值。那麽把這個值改為a[i], 即? d[f[i]] = min(a[i], d[f[i]]);? 終於ans的最大值即為答案。

  代碼例如以下:

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

using namespace std;

const int MAXN = 200050;
const int INF = 1 << 30;
int a[MAXN], f[MAXN], g[MAXN], d[MAXN];

int main()
{
    int t, n, i;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
            scanf("%d", &a[i]);

        f[1] = 1;
        for(i = 2; i <= n; i++)
            if(a[i] > a[i - 1])
                f[i] = f[i - 1] + 1;
            else
                f[i] = 1;

        g[n] = 1;
        for(i = n - 1; i > 0; i--)
            if(a[i] < a[i + 1])
                g[i] = g[i + 1] + 1;
            else
                g[i] = 1;

        int ans = 0;
        for(i = 0; i <= n; i++)
            d[i] = INF;         //d[i]的值所有賦值為INF,方便二分查找和更新d[i]
        for(i = 1; i <= n; i++)
        {
            int len = (lower_bound(d + 1, d + 1 + i, a[i]) - (d + 1)) + g[i];
            ans = max(len, ans);
            d[f[i]] = min(a[i], d[f[i]]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

uva1471