我有一个数组a[n]
。我们输入了数字n
。如果满足以下条件,我需要找到a[i]
和a[j]
的最小乘积:
1)abs(i - j) > k
2)a[i] * a[j]
最小化
这是我的解决方案(非常幼稚):
#include <iostream>
using namespace std;
#define ll long long
int main() {
ll n,k; cin >> n >> k;
ll a[n]; for(ll i=0;i<n;i++) cin >> a[i];
ll mn; bool first = true;
for(ll i=0;i<n;i++) {
for(ll j=0;j<n;j++) {
if(i!=j)
if(abs(i-j) > k) {
if(first) {
mn = a[i]*a[j];
first = false;
} else if(a[i]*a[j] < mn) mn = a[i]*a[j];
}
}
}
cout << mn << endl;
}
但是我想知道是否有更快的方法来找到具有距离的最小产品?
答案 0 :(得分:12)
假设至少有一对满足条件的元素,并且其中两个元素没有相乘,那么可以在curl -X POST https://url.com -H 'authorization: Token YOUR_Session_TOKEN' -H 'content-type: application/json' -d '{"app_ids":["com.exmaple.app"], "data" : {"title":"Title", "content":"Content"}}
时间和Theta(n-k)
空间的最坏情况和最佳情况下完成此操作,像这样:
Theta(1)
就时间和空间的渐近最坏情况复杂度而言,这是最佳的,因为最佳乘积可能是auto back_max = a[0];
auto back_min = a[0];
auto best = a[0]*a[k+1];
for(std::size_t i=1; i<n-(k+1); ++i) {
back_max = std::max(back_max, a[i]);
back_min = std::min(back_min, a[i]);
best = std::min(best, std::min(a[i+k+1]*back_max, a[i+k+1]*back_min));
}
return best;
,且任何a[0]
元素的距离至少为n-(k+1)
,因此解决该问题的任何算法都至少需要读取k+1
个整数。
该算法的思想如下:
最优产品使用n-(k+1)
的两个元素,假设它们是a
和a[r]
。在不失一般性的前提下,我们可以假设a[s]
是乘积,是可交换的。
由于限制s > r
,这意味着abs(s-r) > k
。现在s >= k+1
可能是满足此条件的每个索引,因此我们遍历这些索引。那是所示代码中s
上的迭代,但是为了方便起见,它被i
移了(没关系)。对于每次迭代,我们需要找到以k+1
作为最大索引的最优乘积,并将其与先前的最佳猜测进行比较。
与i+k+1
配对的可能索引由于距离要求而小于或等于i+k+1
。我们也需要对所有这些进行迭代,但是这是不必要的,因为固定的i
上a[i+k+1]*a[j]
超过j
的最小值等于i
产品(在min(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))
的最小值和最大值中取最小值,说明a[j]
的两个可能的符号,或者等效地是两个单调性的两个可能的方向。)
由于我们在此处对其进行优化的a[i+k+1]
值集仅为a[j]
,在{a[0], ..., a[i]}
的每次迭代中仅增加了一个元素(a[i]
),因此我们如果i
大于或小于先前的最佳值,则可以通过更新单个变量来简单地跟踪max(a[j])
和min(a[j])
。这是在代码示例中使用a[i]
和back_max
完成的。
迭代的第一步(back_min
)在循环中被跳过,而是作为变量的初始化执行。
答案 1 :(得分:6)
不确定最快。
对于没有 i
因此,(以下内容太复杂了,请参见walnut's answer)
(•如果k≤n
•将minProduct初始化为a [0] * a [k + 1])
答案 2 :(得分:4)
“最小幅度”
找到2个“最小幅度”元素,然后(找到两个零或搜索整个数组之后),将它们相乘。
不包含abs(i - j) > k
部分的“最低价值”
有3种可能性:
两个最高(最小的)负数
两个最低(最小数量级)的非负数
最低(最大)负数和最高(最大)非负数
您可以搜索所有6个值并找出产品,最后一个是最好的。
但是;一旦您看到零,就知道您不需要再了解前两种可能性。并且一旦看到一个负数和一个非负数,便知道您只在乎第三个可能性。
这将导致具有3个状态的有限状态机-“关心所有3种可能性”,“除非看到负数,否则答案为零”和“仅关心最后一种可能性”。这可以实现为一组3个循环,其中(有限状态机的)状态改变时,其中2个循环跳到另一个循环的中间(goto
。
具体来说,它看起来可能有点模糊(未经测试):
// It could be any possibility
for(ll i=0;i<n;i++) {
if(a[i] >= 0) {
if(a[i] < lowestNonNegative1) {
lowestNonNegative2 = lowestNonNegative1;
lowestNonNegative1 = a[i];
}
if(lowestNonNegative2 == 0) {
goto state2;
}
} else {
if(a[i] > highestNegative1) {
highestNegative2 = highestNegative1;
highestNegative1= a[i];
}
if(lowestNonNegative1 < LONG_MAX) {
goto state3;
}
}
}
if(lowestNonNegative2 * lowestNonNegative1 < highestNegative2 * highestNegative1) {
cout << lowestNonNegative2 * lowestNonNegative1;
} else {
cout << highestNegative2 * highestNegative1;
}
return;
// It will be zero, or a negative and a non-negative
for(ll i=0;i<n;i++) {
state2:
if(a[i] < 0) {
goto state3;
}
}
cout << "0";
return;
// It will be a negative and a non-negative
for(ll i=0;i<n;i++) {
state3:
if(a[i] < lowestNegative) {
lowestNegative = a[i];
} else if(a[i] > highestNonNegative) {
highestNonNegative = a[i];
}
}
cout << lowestNegative * highestNonNegative;
return;
使用abs(i - j) > k
部分的“最低价值”
在这种情况下,您仍然有3种可能性;并可以使其与“具有有限状态机的3个循环”相同的方法工作,但是它太凌乱/难看。对于这种情况,一个更好的选择可能是对阵列进行预扫描,以确定是否有零,以及它们是负数还是正数。这样,在进行预扫描后,您可以知道答案为零,也可以选择仅针对特定可能性设计的循环。