考虑这个例子
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,那么我们可以简单地暴力枚举
min
,
min-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!)
使用上述代码尝试此测试用例:
它强制 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了...