51nod 1153 选择子序列 (好题)

来源:互联网 发布:jq 数组包含 编辑:程序博客网 时间:2024/06/06 02:33

这里写图片描述

这个题很不错。
采用贪心的思想:首先考虑到在整个序列中取最大值一定是最优的,也就是说B数组的第一个数是A数组的中最大值的下标。而且通过题意我们发现,一旦选定了某个数就把当前的区间划分成两份。比如说第一次选了9,第二次选了10, 那么下一次肯定不会选8(这几个数字说的都是下标),不会垮区间选数,那么我们就可以每次选一个区间最大值,再把区间划分成两份分别查找最大值就可以了。因为每个元素最多访问一遍,再加上线段树找最大值,就是nlong的复杂度了

#include<cstdio>#include<algorithm>using namespace std;typedef long long ll;const int maxn = 1e5;int n;struct p{    ll id, val;}a[maxn * 2];void build(int root, int l, int r){    if(l == r){        scanf("%lld", &a[root].val);        a[root].id = l;        return;    }    int mid = (l + r) >> 1;    build(root << 1, l, mid);    build(root << 1|1, mid + 1, r);    if(a[root << 1].val > a[root << 1|1].val){        a[root].val = a[root << 1].val;        a[root].id = a[root << 1].id;    }else {        a[root].val = a[root << 1|1].val;        a[root].id = a[root << 1|1].id;    }}struct p qu(int root, int l, int r, int s, int t){    p x, y;    x.val = y.val = -1e10;    if(s <= l && t >= r){        return a[root];    }    int mid = (l + r) >> 1;    if(s <= mid) x = qu(root << 1, l, mid, s, t);    if(t > mid) y = qu(root << 1|1, mid + 1, r, s, t);    if(x.val > y.val) return x;    return y;}int dfs(int l, int r){    p t;    if(l == r) return 1;    if(l > r) return 0;    t = qu(1, 1, n, l, r);    return max(dfs(l, t.id - 1), dfs(t.id + 1, r)) + 1;}int main(){    scanf("%d", &n);    build(1, 1, n);    printf("%d\n", dfs(1, n));    return 0;}

2017/7/31 更新:
因为这个题目在大佬收录的“o(n)可以过的好题”里面,我就重写了一下,用单调栈代替了线段树。效率提高了一些吧

#include<cstdio>#include<algorithm>#include<stack>using namespace std;typedef long long ll;const int maxn = 1e5;int a[maxn];int left[maxn], right[maxn];int n;stack<int> s;int dfs(int t){    if(t == 0) return 0;    if(left[t] == 0 && right[t] == 0) return 1;    return max(dfs(left[t]), dfs(right[t])) + 1;}int main(){    int t, pos;    scanf("%d", &n);    for(int i = 1; i <= n; i++){        scanf("%d", &a[i]);        pos = a[i] > t ? i : pos;        t = i == 1 ? a[i] : max(a[i], t);        while(!s.empty() && a[s.top()] < a[i]){            left[i] = s.top();            s.pop();        }        s.push(i);    }    for(int i = n; i >= 1; i--){        while(!s.empty() && a[s.top()] < a[i]){            right[i] = s.top();            s.pop();        }        s.push(i);    }    printf("%d\n", dfs(pos));    return 0;}