无法理解算法

10

这是问题的链接 https://www.hackerrank.com/challenges/equal

我读了它的编辑并无法理解。如果您没有在hackerrank上创建帐户,则肯定不会看到其编辑,因此这里是编辑的一些内容。

这等效于说,克里斯蒂可以通过1、2或5拿走一个同事的巧克力,同时保持其他人的巧克力不变。让我们考虑将同事的巧克力减少作为一种操作。为了最小化操作次数,我们应该尝试使每个同事的巧克力数量都等于组中最小的巧克力(min)。我们必须将第i个人A[i]的巧克力数量减少(A[i] - min)。让这个值为x。

This can be done in k operations.

k = x/5 +(x%5)/2 + (x%5)%2 

但是从这里开始我无法理解。

令 f(min) 为所有同事将各自的巧克力减少到最小值所执行的操作总和。然而,有时候 f(min) 可能不一定给出正确的答案。也可能存在这样的情况:

f(min) > f(min-1)

f(min) < f(min-5)

当同事的数量为N时,f(min-5)比f(min)多花费N次操作。因此,如果

A = {min,min-1,min-2,min-3,min-4}
then f(A) <= f(min) < f(min-5)

有人能帮我理解为什么需要检查f(min),f(min-1),...,f(min-4)吗?

1个回答

10
考虑这个例子A = [1,5,5]
像题解所说的,直觉上认为将A更改为[1,1,1]需要4次操作(2次减去2),但是更改为[0,0,0]只需要3次操作(1次减去1和2次减去5)更好。
因此,如果min = 数组中的最小元素,则更改所有元素为min可能不是最优的。
你不理解的部分是要适应这种情况,我们知道min可能不是最优的,因为min-x可能更好,但是x有多大呢?嗯,它是4。题解表示,如果我们知道x最多为4,那么我们可以简单地暴力枚举minmin-1......min-4来查看哪个是最小值,而无需思考太多。 x <= 4 的推论(非证明!) 如果 x >= 5,则您必须对所有元素使用至少额外的 N 类型 3 (减5)操作,这肯定不值得。
基本上,这不是操作类型的问题,而是因为您需要在所有元素上使用相同的操作,在您这样做之后,问题并没有减少,元素之间的相对差异仍然相同,而您的目标是使相对差异为0,您白白浪费了N次操作。
换句话说,如果 x >= 5,则x-5必须是更优选择的目标,事实上x%5必须是最佳目标。

(以下是TL;DR部分:版本2) 如果你对证明不感兴趣,请直接跳到最后一部分。

在编写原始解决方案的过程中,我怀疑x <= 2 的确,我尝试在HackerRank上提交一个代码,只检查f(min-x) where x <= 2的最小值,并获得AC 。
更正式地说,

如果 5> (z-min)%5 >= 3 and (z-min')%5==0, 则 F(min')< F(min) 其中 min'=min-x for x<=2, F(k) = element z 变为 k 的最少操作次数

(注意符号,我使用F()表示不同于问题中的f() 这里是证明:
如果(z-min)%5 = 1或2,则至少需要(z-min)/5 + 1次操作,而(z-min')%5 == 0 需要 (z-min')/5 = (z-min)/5 + 1次操作,意味着F(min') = F(min)
如果(z-min)%5 == 3或4,则至少需要(z-min)/5 + 2次操作,而(z-min')%5 == 0 需要 (z-min')/5 = (z-min)/5 + 1次操作,意味着F(min') < F(min) (或 F(min') = F(min)+1)
所以我们证明:

如果5> (z-min)%5 >= 3且(z-min')%5==0,则F(min')< F(min) 其中min'=min-x

现在让我们证明x的范围。
由于我们假设(z-min)%5 >= 3且(z-min')%5 == 0
因此(z-min')%5 = (z-min+x)%5 = ((z-min)%5 + x%5)%5 == 0
现在,如果x >= 3,那么为了使((z-min)%5 + x%5)%5 == 0(z-min)%5永远不能是>= 3。
如果x = 2,那么(z-min)%5可以是3;如果x = 1,那么(z-min)%5可以是4,以满足条件:5> (z-min)%5 >= 3且(z-min')%5==0
因此,我们一起展示:

如果5> (z-min)%5 >= 3且(z-min')%5==0,则F(min')< F(min) 其中min'=min-x for x<=2


请注意,您总是可以生成数组P,使得f(min') < f(min),因为您总是可以重复可以通过这种方法改进的整数,直到它们超过那些无法改进的整数。这是因为对于无法改进的元素,它们始终需要恰好1次操作。
例如:令P = [2,2,2,10],则f(min) = 0 + 3 = 3,f(min-2) = 3 + 2 = 5。

这里的10是可以改进的元素,而2不能,因此我们只需在数组中添加更多的10。每个2将使用1次操作来到达min' = min-2,而每个10将节省1次操作以获得min'。因此,我们只需要添加更多的10,直到它们超过2的“浪费”为止:

P = [2,2,2,10,10,10,10,10],那么f(min) = 0+15 = 15,f(min-2) = 3+10=13

或者简单地

P = [2,10,10],f(min) = 6,f(min-2) = 5

(TL;DR部分结束!)


编辑

天哪,在HackerRank上的测试用例太弱了!

故事是今天早上我到办公室时,一直在想这个问题,并认为我的代码可能有问题(但已经AC了!)

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int T, n, a[10005], m = 1<<28;

int f(int m){
    m = max(0, m);
    int cnt = 0;
    for(int i=0; i<n;i++){
        cnt += (a[i]-m)/5 + (a[i]-m)%5/2 + (a[i]-m)%5%2;
    }
    return cnt;
}

int main() {
    cin >> T;
    while(T--){
        m = 1<<28;
        cin >> n;
        for(int i=0; i<n;i++) cin >> a[i], m = min(m,a[i]);

        cout <<  min(min(f(m), f(m-1)),f(m-2)) << endl;
    }
    return 0;
}

你能看到问题吗?
问题在于m = max(0, m);
它确保min-x至少为0,但是等等,我上面的证明并没有说min-x的范围!实际上它可以是负数!
记住原始问题是关于“加”的,因此目标没有最大值;虽然我们将问题建模为“减”,但目标也没有最小值(但我将其设为0!)
使用上述代码尝试此测试用例:

1
3
0 3 3

它强制 min-x = 0,所以它输出4,但答案应该是3。

(如果我们使用“添加”模型,则目标应为10,其中在a [0]、a [2]上+5,在a [0]、a [1]上+5,在a [1]、a [2]上+2)

因此,当我删除 m = max(0, m); 这行时,一切最终都变得正确(我想...),它允许 min-x 变为负数并给出3作为正确的输出,当然新代码也被ACed了...


1
虽然这不是一个完美的证明,但理想情况下应该证明另一方面:如果f(min') < f(min),那么(z-min)%5 >= 3且(z-min')%5 == 0对于x<=2...但是我在起草这部分时遇到了困难,希望有人能够完成我的解决方案... :) - shole
你说过,“这不是操作类型的问题,而是因为你需要在所有元素上使用相同的操作”。那么难道我们不需要N个类型1(减1)操作来得到min-1吗?同样地,我们不应该只考虑min吗?我确定我错过了什么,因为我们确实需要考虑min、min-1、min-2、min-3和min-4。 - HCSF
@HCSF 不,那不是我的意思。为了将所有数字达到最小值-1,并不一定需要使用N个类型1的操作,例如[1, 5, 5] ==> 最小值-1为0,而f(最小值-1) = 3,可以使用1个类型1操作和2个类型3操作。我想表达的观点是对于[6, 10, 10],如果有人认为最优解是达到最小值-6而不是最小值-1,那就是荒谬的,因为[6, 10, 10]可以通过在所有元素上执行类型3操作等效于[1, 5, 5],这并没有减少它们之间的相对差异。如果能够达到最小值-6,那么使用更少的3个操作就可以达到最小值-1。 - shole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接