我试图解决一个问题而且遇到了一些我未能解决的障碍,从这里开始的问题是:Codeforces - 817D
现在我尝试强制它,使用我可以生成的数组的每个段的基本get min和max然后跟踪它们我减去它们并将它们加在一起以获得最终的不平衡,这看起来不错但是它给了我一个时间限制超过因为暴力迫使n *(n + 1)/ 2子阵列给定n是10 ^ 6,所以我只是没有绕过它并且在几个小时之后没有得到任何新的想法我决定看到一个我无法理解的解决方案:/,这是解决方案:
#include <bits/stdc++.h>
#define ll long long
int a[1000000], l[1000000], r[1000000];
int main(void) {
int i, j, n;
scanf("%d",&n);
for(i = 0; i < n; i++) scanf("%d",&a[i]);
ll ans = 0;
for(j = 0; j < 2; j++) {
vector<pair<int,int>> v;
v.push_back({-1,INF});
for(i = 0; i < n; i++) {
while (v.back().second <= a[i]) v.pop_back();
l[i] = v.back().first;
v.push_back({i,a[i]});
}
v.clear();
v.push_back({n,INF});
for(i = n-1; i >= 0; i--) {
while (v.back().second < a[i]) v.pop_back();
r[i] = v.back().first;
v.push_back({i,a[i]});
}
for(i = 0; i < n; i++) ans += (ll) a[i] * (i-l[i]) * (r[i]-i);
for(i = 0; i < n; i++) a[i] *= -1;
}
cout << ans;
}
我试过跟踪它,但我一直想知道为什么使用矢量,我得到的唯一想法是他想将矢量用作堆栈,因为它们的行为相同(几乎)但事实上我不会&#39甚至知道为什么我们需要一个堆栈,这个等式ans += (ll) a[i] * (i-l[i]) * (r[i]-i);
让我感到困惑,因为我不知道它来自哪里。
答案 0 :(得分:0)
那就是计算的野兽。我必须承认,我完全不理解它。蛮力解决方案的问题是,您必须计算值或重新计算值。
在略微修改的示例中,您为2, 4, 1
的输入计算以下值(我按“距离”重新排序)
[2, *, *] (from index 0 to index 0), imbalance value is 0; i_min = 0, i_max = 0
[*, 4, *] (from index 1 to index 1), imbalance value is 0; i_min = 1, i_max = 1
[*, *, 1] (from index 2 to index 2), imbalance value is 0; i_min = 2, i_max = 2
[2, 4, *] (from index 0 to index 1), imbalance value is 2; i_min = 0, i_max = 1
[*, 4, 1] (from index 1 to index 2), imbalance value is 3; i_min = 2, i_max = 1
[2, 4, 1] (from index 0 to index 2), imbalance value is 3; i_min = 2, i_max = 1
where i_min and i_max are the indices of the element with the minimum and maximum value.
For a better visual understanding, i wrote the complete array, but hid the unused values with *
因此,在最后一种情况[2, 4, 1]
中,暴力搜索所有值的最小值,这是不必要的,因为您已经通过计算{{{}来计算问题子空间的值。 1}}和[2,4]
。但仅比较这些值是不够的,还需要跟踪最小和最大元素的索引,因为在计算[4,1]
时,这些可以在下一步中重复使用。
这背后的理念是一个名为dynamic programming的概念,其中存储计算结果以便再次使用。通常,您必须在速度和内存消耗之间进行选择。
所以回到你的问题,这就是我的理解:
[2, 4, 1]
和l
用于存储当前数字左侧或右侧最大数字的索引r
用于查找大于当前数字(v
)的最后一个数字(及其索引)。它跟踪数字序列的上升,例如:对于输入a[i]
,首先存储5,3,4
,然后存储5
,当3
出现时,弹出3,但需要5的索引(要存储在4
)l[2]
)。最大(和第二次运行最小)元素的存储索引与值ans += (ll) a[i] * (i-l[i]) * (r[i]-i)
一起计算,这对我来说现在没有多大意义,但似乎有用(对不起)。a[i]
中的所有值都乘以a
,这意味着,旧的最大值现在是最小值,并且计算再次完成(外部的第二次运行 - 循环过j)这最后一步(将-1
乘以a
)和外部for循环遍历j不是必需的,但这是重用代码的一种优雅方式。
希望这有点帮助。