一个有趣的题目

来源:互联网 发布:特价机票 知乎 编辑:程序博客网 时间:2024/05/20 16:34

给个数列A,问是否存在Ai<Ak<Aj(s.t.i<j<k).
这题是我在面试的时候碰到的题目,当时没想到O(n)解,所以当时假设没有O(n)解。今天早上上班的路上,又想了一下这道题。发现确实有O(n)解,特地写了这个题解。
首先,由于i位于最左边,所以首先可以处理leftj=min(A1,A2,...,Aj1)leftj<Aj。显然对于Aj需要求一个Ak>leftj && Ak<Aj
其次,需要强调一点,left数组是一个单调不增的数列。于是考虑从右往左扫描数列,维护一个单调递减栈,维护时,对于当前放入栈的Aj, 将栈顶小于Aj的数弹出。于是在维护单调栈的同时,判断栈顶元素是否是满足的Ak即可。即stack.top()>leftj && stack.top()<Aj
这种做法确实是一个必要解,现在证明充分性。
证明:现在证明,对于Aj来说Ak(k>j)在放入栈中时,不会因为将在栈顶元素s(s>leftj && s<Aj)弹出,而导致无解。
1.假如Aj>Ak,显然如果栈顶元素s弹出,Ak入栈,那么对于Aj来说,Ak是一个满足解,且对于Aj来说更优。
2.假如Aj<=Ak,显然,如果存在栈顶元素s。由于j<k,那么leftj>=leftk,显然会有leftk<=leftj<s<Aj<=Ak.于是s>leftk && s<Ak,于是s对于Ak是一个满足解。
而如果s<=leftk,则显然可以弹出。s>=Ak, 显然可以直接将Ak放入栈顶。
所以得证。

#include <bits/stdc++.h>using namespace std;bool solve(const vector<int>& A) {  vector<int> left(A.size());  left[0] = 0x3f3f3f3f;  for (int i = 1; i < A.size(); ++i) {    left[i] = min(left[i - 1], A[i - 1]);  }  stack<int> S;  for (int i = A.size() - 1; --i) {    if (S.empty()) {      S.push(A[i]);    } else {      if (left[i] < A[i] && left[i] < S.top() && S.top() > A[i]) {        return true;      } else {        while (!S.empty() && S.top() < A[i]) {          S.pop();        }        S.push(A[i]);      }    }  }  return false;}
原创粉丝点击