试图理解这个解决方案

Trying to understand this solution

本文关键字:解决方案      更新时间:2023-10-16

我试图解决一个问题,我遇到了一些我未能解决的障碍,从这里开始是问题: 代码部队 - 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;
}

我尝试跟踪它,但我一直想知道为什么使用向量,我得到的唯一想法是他想将向量用作堆栈,因为它们的行为相同(几乎),但事实上我什至不知道为什么我们需要一个堆栈在这里和这个方程ans += (ll) a[i] * (i-l[i]) * (r[i]-i);真的让我感到困惑,因为我不知道它来自哪里。

嗯,这是一个计算的野兽。我必须承认,我也不完全理解它。蛮力解决方案的问题在于,您必须重新计算值或重新计算。

在一个稍作修改的示例中,您计算输入的以下值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],bruteforce会查找所有值的最小值,这不是必需的,因为您已经通过计算[2,4][4,1]来计算问题的子空间的值。但是仅比较值是不够的,您还需要跟踪最小和最大元素的索引,因为它们可以在下一步计算[2, 4, 1]时重用。

这背后的想法是一个称为动态规划的概念,其中计算结果被存储以供再次使用。通常,您必须在速度和内存消耗之间进行选择。

所以回到你的问题,这是我的理解:

  • 数组lr用于存储当前数组左侧或右侧最大数量的索引
  • 向量v用于查找大于当前数字(a[i])的最后一个数字(及其索引)。它跟踪上升的数字序列,例如,对于输入5,3,4首先存储5,然后存储3,当4到来时,弹出 3,但需要索引 5(存储在l[2]中)
  • 然后是这个花哨的计算(ans += (ll) a[i] * (i-l[i]) * (r[i]-i))。最大(在第二次运行中是最小值)元素的存储索引与值一起计算a[i]现在对我来说没有多大意义,但似乎有效(抱歉)。
  • 最后,数组a中的所有值都乘以-1,这意味着,旧的最大值现在是最小值,并且再次进行计算(在 j 上第二次运行外部 for 循环)

最后一步(a乘以-1)和 j 上的外部 for 循环不是必需的,但这是一种重用代码的优雅方式。

希望这有所帮助。

相关文章: