如何快速找到给出最大值的点?请使用Java或C ++代码

问题描述

我需要一种快速方法来在间隔重叠时找到最大值,这与找到重叠最多的点不同,那就是“顺序”。我将有int[][] data中的int[]中有2个值,其中第一个数字是中心,第二个数字是半径,越靠近中心,该点的值将越大。例如,如果我得到如下数据:

int[][] data = new int[][]{
{1,1},{3,3},{2,4}};

然后在数字行上,它是这样的:

x axis: -2 -1 0 1 2 3 4 5 6 7  
   1 1:       1 2 1  
   3 3:       1 2 3 4 3 2 1  
   2 4:  1  2 3 4 5 4 3 2 1  

因此,要使我的点的值尽可能大,我需要选择点x = 2,这得出的总值为1 + 3 + 5 = 9,这是最大可能的值。有没有办法快速做到这一点?像O(n)或O(nlogn)的时间复杂度

解决方法

这可以通过简单的O(n log n)算法来完成。

考虑值函数v(x),然后考虑其离散导数dv(x)= v(x)-v(x-1)。假设您只有一个间隔,例如{3,3}。 dv(x)从-infinity至-1为0,然后从0至3为1,然后从4至6为-1,然后从7至infinity为0。也就是说,导数在-1之后紧随其后变化1,在3之后紧随-2以及6之后紧随其后变化1。

对于n个间隔,存在3 * n个导数更改(其中一些可能在同一点发生)。因此,找到所有派生更改(x,change)的列表,按它们的x对其进行排序,然后遍历集合。

看哪:

intervals = [(1,1),(3,3),(2,4)]

events = []
for mid,width in intervals:
    before_start = mid - width - 1
    at_end = mid + width
    events += [(before_start,(mid,-2),(at_end,1)]

events.sort()

prev_x = -1000
v = 0
dv = 0

best_v = -1000
best_x = None

for x,change in events:
    dx = x - prev_x
    v += dv * dx
    if v > best_v:
        best_v = v
        best_x = x
    dv += change
    prev_x = x

print best_x,best_v

还有Java代码:

 TreeMap<Integer,Integer> ts = new TreeMap<Integer,Integer>();
        
    for(int i = 0;i<cows.size();i++) {
        int index = cows.get(i)[0] - cows.get(i)[1];
        if(ts.containsKey(index)) {
            ts.replace(index,ts.get(index) + 1);
        }else {
            ts.put(index,1);
        }
            
        index = cows.get(i)[0] + 1;
        if(ts.containsKey(index)) {
            ts.replace(index,ts.get(index) - 2);
        }else {
            ts.put(index,-2);
        }
            
        index = cows.get(i)[0] + cows.get(i)[1] + 2;
        if(ts.containsKey(index)) {
            ts.replace(index,ts.get(index) + 1);
        }else {
                ts.put(index,1);
        }
    }
        
    int value = 0;
    int best = 0;
    int change = 0;
    int indexBefore = -100000000;
        
    while(ts.size() > 1) {
        int index = ts.firstKey();
        value += (ts.get(index) - indexBefore) * change;
        best = Math.max(value,best);
        change += ts.get(index);
        ts.remove(index);
    }

其中cows是数据

,

嗯,一般的O(n log n)或更棘手,可能可以通过线性编程解决,但这会变得相当复杂。

经过一些争论,我认为可以通过线相交和函数求和(以线段相交表示)来解决。基本上,将每个都视为直线顶部的三角形。如果输入为(C,R),则三角形以C为中心,半径为R。线上的点是C-R (value 0)C (value R)C+R (value 0)。三角形的每个线段代表一个值。

考虑任何2个这样的“三角形”,最大值出现在2个位置之一:

  • 其中一个三角形的峰值
  • 三角形的交点或两个三角形整体的交点。多个三角形仅意味着更多可能的相交点,可悲的是可能的相交数呈平方增长,因此,除非有相交的数量为{{1 }}或更少。

要找到所有相交点,我们可以为此使用标准算法,但是我们需要以一种特定的方式进行修改。我们需要添加一条线,该线从每个峰延伸得足够高,因此它将比任何线都高,因此基本上是从(C,C)到O(N log N)。然后,我们运行该算法,输出敏感的相交查找算法为O(N),其中(C,Max_R)是相交的数量。可悲的是,它可能高达O(N log N + k)(考虑大小写k,依此类推,直到O(N^2)。每条线都会与其他每条线相交。一旦有了(1,100),100)...交集在每个交叉点,您都可以通过对队列中所有点的求和来计算该值。运行总和可以保留为缓存值,因此它只更改(50,100)次,尽管可能不可行。改为O(N + K)。将其设置为潜在的O(K)(在O(N*K)最坏的情况下)代替:(。尽管这似乎是合理的。对于每个交叉点,您需要总结为O(N^3)行可以获取该点的价值,尽管在实践中,可能会获得更好的性能。

考虑到您的目标是最大化而不是仅仅找到交叉点,可以进行一些优化。可能会有一些不值得追求的交叉路口,但是,我也看到了这样的情况:距离太近了,无法将其削减。让我想起了凸包。在许多情况下,您可以轻松地减少90%的数据,但在某些情况下,您会看到最差的结果(每个点或几乎每个点都是船体点)。例如,在实践中,肯定有一些原因可以确保您的总和将小于当前的已知最大值。

另一种优化方法可能是构建间隔树。