【发布时间】:2011-05-06 04:44:59
【问题描述】:
我最近从一位朋友那里听到这个问题,他在一次采访中被问到这个问题。他无法弄清楚,我也没有找到任何有效的解决方案。我希望这里有一位算法专家可以向我展示一种新方法
问题:
给定一个数组 A 和一个数 S',提供一个有效的算法(nlogn)来找到一个数 K是S'。
例如,给定A: [90,30,100,40,20] 和S' = 210,K 将是60。
【问题讨论】:
我最近从一位朋友那里听到这个问题,他在一次采访中被问到这个问题。他无法弄清楚,我也没有找到任何有效的解决方案。我希望这里有一位算法专家可以向我展示一种新方法
问题:
给定一个数组 A 和一个数 S',提供一个有效的算法(nlogn)来找到一个数 K是S'。
例如,给定A: [90,30,100,40,20] 和S' = 210,K 将是60。
【问题讨论】:
用 Python 编写,即使你不懂该语言也应该相当易读:
#!/usr/bin/env python
A = [90, 30, 100, 40, 20]
S = 210
K = 60
A = sorted(A)
prev = 0
sum = 0
for index, value in enumerate(A):
# What do we need to set all subsequent values to to get the desired sum?
solution = (S - sum) / (len(A) - index)
# That answer can't be too big or too small.
if prev < solution <= value:
print solution
sum += value
prev = value
结果:
60
排序是O(n log n),循环是O(n)。因此将算法作为一个整体组合为 O(n log n)。
【讨论】:
首先将列表从小到大排序,然后找出它有多长。然后开始将数字一一相加。在每个步骤中,还要找到总和的下限 - 如果您尚未添加的所有其余数字与当前数字相同,则整个列表的总和将是多少。
在某些时候,总和的下限将从小于 S' 变为大于 S',此时您可以进行一些算术运算来确定截止值应该是多少。例如(C = 当前总和,L = 总和的下限):
开始 [90 30 100 40 20] 种类 [20 30 40 90 100] 开始加起来 C1 = 20 L1 = 20 + 4*20 = 100 210 //太大了! S' = C3 + K*2 所以 K = (S'-C3)/2 = 60【讨论】:
这是我的解决方案。我基本上是在 [0, max(A)] 范围内对 K 的值进行二进制搜索。虽然这避免了必须首先对数组进行排序(从而保留原始数组),但它仍然是 O(n*log(k)),其中 n 是 A 中的元素数,k 是 A 中的最大值。
#! /usr/bin/env python
from itertools import imap
def findK(A,S):
lo,hi=0,max(A)
while lo<hi:
mid=(hi+lo+1)>>1
result=sum(imap(lambda x: x if x<mid else mid,A))
if result<=S:
lo=mid
else:
hi=mid-1
return lo
if __name__=='__main__':
print findK(A=[90,30,100,40,20],S = 210)
【讨论】:
这可以通过使用线性时间选择的变体来实现,而无需在 O(n) 时间内进行排序,如下所示(请注意,while 循环的迭代的运行时间形成一个几何级数 - 分区子例程分割一个范围一个数组,从下到上,分成小于或大于rank mid的元素,运行时间与数组范围的大小成正比):
foo(x, s) {
sumBelow = 0;
lower = 0;
upper = x.size();
while (lower + 1 != upper) {
mid = (upper + lower) / 2;
partition(x, lower, mid, upper); // O(upper - lower) time
sumb = 0;
maxb = 0; // assuming non-negative to avoid use of flags
for (i = lower; i < mid; i++) {
sumb += x[i];
maxb = max(maxb, x[i]);
}
if (sumBelow + sumb + maxb * (x.size() - mid) <= s) {
lower = mid;
sumBelow += sumb;
} else {
upper = mid;
}
}
K = (s - sumBelow) / (x.size() - lower);
if (K > maxElement(x)) return error();
else return K;
}
【讨论】:
partition(x, lower, upper),因为我认为mid实际上是不需要的。这会让你的观点更清楚。
nlogn 排序
int[] sortedArray;
int s;
int sum=0;
for(int i=0; i<sortedArray.length; i++){
sum = sum + sortedArray[i];
if((s - sum)/(sortedArray.length - i) < sortedArray[i+1]){
return (s - sum)/(sortedArray.length - i);
}
}
【讨论】:
好吧,看起来我迟到了,但无论如何希望这个算法有意义。
最后,N/P 应该会给你一个完美的答案,它的时间复杂度为 O(N),空间复杂度为 O(1)。
在这里找到执行代码:http://ideone.com/MDL3iy
import java.util.Scanner;
class Ideone {
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
int S = in.nextInt();
int[] array = {90,30,100,40,20};
int len = array.length;
int sAvg = S/len;
int trackSmallerThanAverage = 0;
int countMorethanAverage = 0;
for(int i=0; i<len; i++) {
if(array[i] > sAvg) {
countMorethanAverage ++;
} else if (array[i]<sAvg) {
trackSmallerThanAverage += array[i];
}
}
int finalValue = ( S - trackSmallerThanAverage )/countMorethanAverage;
System.out.println(finalValue);
in.close();
}
}
【讨论】: