Description
一堆数字,必须从左到右且全部取出,从下往上放,满足下层 上层, 求所能得到的最高层。如下图为两层:
样例解释:
3 1 2 3
第一层(最下面)放1 2,第二层放3,答案为2
Solution
瞎贪心肯定是不可取的,但是需要知道一个事实:在构造的过程中,在相同高度下,最底层的数字和越小,结果最优。
理性的感受一下就是:最底层的数字和越小,越能够将剩余的放到上面,从而使得最终高度越高。
给出一组数据
5 5 4 3 2 100
可能在分析的的时候出现 100 无处可放的情况, 不妨从后往前(即从上往下放),需要满足递增
即变成
5 100 2 3 4 5
令 表示到了第 个数字的时候,当前底层以第 个数字结尾时,所能达到的最大高度。
我们用一个 记录此时最底层的数量。
那么肯定存在一个最优转移点 满足
有限制条件
由结论:在相同高度下,最底层的数字和越小,结果最优,得知需要找到 里找到最大的
对于 具有上界
维护一个递增的单调队列,每次找到最大的满足条件的 即可。
时间复杂度
#include<bits/stdc++.h> typedef long long ll; using namespace std; const int N = 1e6 + 5; const int mod = 1000000007; /* 相同高度下底层越小越好 */ void solve() { int n; cin >> n; vector<int> a(n + 1, 0), dp(n + 1, 0), d(n + 1, 0); vector<ll> sum(n + 1, 0); for (int i = 1; i <= n; i++) { cin >> a[i]; } reverse(a.begin() + 1, a.begin() + 1 + n); for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; } deque<int> q; int pos = 0; std::function <int(int)> cal = [&] (int id) -> int { return sum[id] + d[id]; }; for (int i = 1; i <= n; i++) { while (!q.empty() && sum[i] >= cal(q.front())) { pos = q.front(); q.pop_front(); } d[i] = sum[i] - sum[pos]; dp[i] = dp[pos] + 1; while (!q.empty() && cal(i) <= cal(q.back())) { q.pop_back(); } q.push_back(i); } cout << dp[n] << '\n'; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; //cin >> T; while(T--) { solve(); } return 0; }