zzuoj-10453: 补题栈

传送门

  “栈”这个数据结构,想必大家都很熟悉,它满足“后进先出”的性质。
  squee_spoon做题的时候,有一种强迫症,即他总是先做最近的题目。例如,squee_spoon还没有解决问题a的时候,看到了问题b,那么他就会暂时放下a,先解决b。我们可以认为,squee_spoon做题总是严格满足栈的性质。然而squee_spoon太弱了,总会有一大堆未解决的问题,在问题积压数达到m的时候,他将难以维护他的“补题栈”,此时他不再接受新的问题(换句话说,他在任意时刻,最多存在m个未解决的问题)。
  现在,squee_spoon拿到了一份题单,上面按顺序列有n道题的题号p1~pn,求squee_spoon按顺序读题号的情况下,可能的字典序最小的做题顺序。

  题目要求字典序最小的做题顺序,所以很容易有个朴素的想法,首先让做的第一道题题号尽可能小,然后让第二道题题号尽可能小……这样得到的顺序肯定是最小的。

  顺着这个思路往下想,有哪些题可以成为第一道呢,显然是前m题都可以,因为“补题栈”的大小是m,你就可以一直读到那一题,然后做掉,问题就转化为了“求数组p在区间[1,m]的最小值”。同理,哪些题可以成为当前能做的题呢,自然是往后的某个区间(区间长度取决于当前栈被占用了多少空间),以及当前的栈顶。

  每次要做的题就是能做的题里面题号最小的那题,也就是相应区间的最小值和栈顶的值中更小的那个。于是这题的姿势就是快速求区间最小值辣!比如线段树(O(nlogn)),spare table(O(nlogn)),分块(O(n^1.5))都是可以过的。下面给一个线段树的解法:

#include <bits/stdc++.h> 
#define N 100010
#define ll long long
using namespace std;
int a[N], seg_tree[N<<2];
void build(int root, int left, int right){
    if (left == right){
        scanf("%d", &a[left]);
        seg_tree[root] = a[left];
        return ;
    }
    int mid = (left+right)>>1;
    build(root<<1, left, mid);
    build(root<<1|1, mid+1, right);
    seg_tree[root] = min(seg_tree[root<<1], seg_tree[root<<1|1]);
}
int query(int root, int left, int right, int L, int R){
    if (left <= L && right >= R){
        return seg_tree[root];
    }
    int mid = (L+R)>>1;
    if (right <= mid)   return query(root<<1, left, right, L, mid);
    else if (left > mid)    return query(root<<1|1, left, right, mid+1, R);
    else return min(query(root<<1, left, right, L, mid), query(root<<1|1, left, right, mid+1, R));
}
stack<int> s;
bool first = true;
void Print(int x){
    if (first){
        first = false;
    }else{
        printf(" ");
    }
    printf("%d", x);
}
int main(){
#ifndef ONLINE_JUDGE 
    freopen("1.txt", "r", stdin);
#endif
    int n, m, i, j;
    cin >> n >> m;
    build(1, 1, n);
    for(i = 1; i <= n; i++){
        int l = i, r = i+m-s.size()-1;
        r = min(r, n);
        int MIN = query(1, l, r, 1, n);
        if (s.size() && s.top() < MIN){
            Print(s.top());
            s.pop();
            i--;
        }else{
            Print(MIN);
            while(a[i] != MIN){
                s.push(a[i]);
                i++;
            }
        }

    }
    while(!s.empty()){
        Print(s.top());
        s.pop();
    }
    puts("");
    return 0;
}

以上内容转自http://blog.csdn.net/squee_spoon/article/details/50985698

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值