一个有趣的题目

给个数列 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 

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;
}

你可能感兴趣的:(乱搞)