【问题标题】:Maximize absolute difference最大化绝对差异
【发布时间】:2016-09-02 10:54:38
【问题描述】:

给定一个硬币数组,每个硬币都有一些价值。数组大小为 N。您可以更改任何硬币的价值,除了第一个和最后一个硬币。您可以将第 i 个硬币的价值更改为总和的一半 (i-1)th 和 (i+1)th 硬币的价值,但要这样做,需要满足两个条件。

(1) 第 (i-1) 和 (i+1) 个硬币的值应该是偶数并且

(2) 如果在第 i 个硬币的价值之后第 j 个硬币的价值发生变化,那么 j 应该大于 i

现在你的任务是最大化第一半数组硬币的值和第二半数组硬币的值之和之间的绝对差。如果数组大小为奇数,则忽略中间元素。

谁能建议我找到答案的算法。

任务是找到最大的绝对差异。

我的算法: 1. 求左半边和右半边的总和 2.如果左半边>右半边通过给出操作最大化左半边并最小化左半边但我没有得到正确的答案。

PS:我一周前参加了一次面试。有人问我无法弄清楚方法。

【问题讨论】:

  • StackOverflow 不是一个家庭作业服务。这句话每天要说多少次? :(
  • 这似乎是个难题。能否提供问题的根源。
  • 是否有任何答案适合您的需求?你能接受或发表评论吗?

标签: arrays algorithm maximize


【解决方案1】:

蛮力算法有一些可能的优化。

我们可以让算法在每个索引处存储从右侧所有数据计算得出的可能总和。该信息将在递归回溯期间填写。

示例:为简化起见,我将只考虑最大化前半部分和最小化后半部分总和。

[4, 1, 8, 2, 4, 1, 4]

该算法将深度优先处理数组的最后一个元素,然后将最佳和存储在那里,只考虑右边的内容。因为右边什么都没有,所以总和是 4。但是因为我们想要最小化,所以我们切换符号。所以我们存储这样的东西:

[4, 1, 8, 2, 4, 1,  4]
                   -4 

然后我们回溯。目前我们有两种可能性:保持 1 不变,或者使用周围值的平均值(即 4):

[4, 1, 8, 2, 4, 1,  4]
                   -4 
               -5
[4, 1, 8, 2, 4, 4,  4]
                   -4
               -8

值 -5 和 -8 都将存储在哈希中,因此对于值 1,我们可以找到 -5,对于值 4,我们可以找到 -8。

在算法的某个点,我们将尝试在第二个数组元素(而不是 1)处使用值 6,然后再次递归。当我们到达最后一个元素时,我们发现可能的值又是 1 或 4(如果左侧没有其他任何更改),因此我们不必更深地递归:我们可以阅读我们在该索引处维护的哈希值的总和。

该系统可以在更大的数组中节省大量成本,确保算法仅在真正需要时才深入递归。显然,它是以空间为代价的。

然后整个算法可以第二次执行,但随后交换符号。在这两个结果中,采用最佳解决方案,使绝对值最大化。

通过将可能改变的元素值作为函数参数传递,我们可以避免创建多个数组,而只使用输入数组。另一方面,在每个索引处创建的散列会占用一些空间。

这是JavaScript中的算法,我没有使用任何花哨的功能,所以应该很容易理解:

function getMaxSum(a) {
    // Index of the element in the middle. If integer,
    // the element at this index will not play a role in any sum:
    var mid = (a.length-1)/2;
    // Two results, one that maximises the left sum and minimises the right sum
    // The other minimises the left and maximises the right:
    var result, result2, b;

    function recurse(prevVal, index, sign, hash = []) {
        var val, nextVal, sum, result, avg;
        
        val = a[index];
        if (index >= a.length-1) {
            // At the last element there are no choices left:
            return { sum: -sign*(prevVal+val), nextVal: val, hash: [] };
        }
        if (!hash[index]) hash[index] = [];
        nextVal = a[index+1];
        result = { sum: -Infinity, nextVal: 0, hash: hash[index] };
        // Loop through the 2 possibilities (in general): take value as is, or 
        // take the average of previous and next value:
        while (true) {
            // If the result from this position onward is not know, calculate
            // it via a recursive call:
            if (!hash[index][val]) hash[index][val] = recurse(val, index+1, sign, hash);
            // Add the previous value to the best sum at this point, using the appropriate sign,
            // and store the result in a hash table, for future reference:
            sum = hash[index][val].sum + (index-1 > mid ? -1 : index-1 < mid ? 1 : 0) * sign * prevVal;
            if (sum > result.sum) {
                result.sum = sum;
                result.nextVal = val;
            }
            if (prevVal % 2 || nextVal % 2 || (avg = (prevVal + nextVal)/2) === val) break;
            val = avg;
        }
        return result;
    }

    // Calculate both results
    result = recurse(a[0], 1, 1, []);
    result2 = recurse(a[0], 1, -1, []);
    // Pick the best one.
    if (Math.abs(result2.sum) > Math.abs(result.sum)) result = result2;

    // Rebuild the array corresponding to the best result:
    b = [a[0]];
    while (result) {
        b.push(result.nextVal);
        result = result.hash[result.nextVal];
    }
    return b;
}

// Sample data
var a = [4, 1, 8, 2, 4, 1, 4];
console.log(' input: ' + a);
// Apply algorithm
var b = getMaxSum(a, 1);
// Print result
console.log('result: ' + b);

【讨论】:

    【解决方案2】:

    我想不出比蛮力更复杂的算法了。您只需尝试所有可能的事情并返回最佳结果。我推荐一种递归方法。编写一个以输入列表和索引为参数的递归函数。该函数尝试更改驻留在该索引上的数字,并将返回最佳结果。最后你应该打印最好的结果。这听起来很复杂,但实际上它相当容易。看看这段代码:

    def func(input_list, index):
        # if we have reached the end of the list
        # we compute the result and return it
        L = input_list[:]
        if index == len(L): 
        # return absolute difference of the two halves
            return abs ( sum(L[:len(L)/2]) - sum(L[int(len(L)/2.0 + 1):]) )
    
        #result of not changing this item
        no_change = func(L, index+1)
    
        #check if it is possible to change this item
        change = 0
        if index-1 >= 0 and index+1 < len(L) and L[index-1]%2==0 and L[index+1]%2==0:
            #result of changing this item if possible
            L[index] = (L[index-1] + L[index+1])/2
            change = func(L, index+1)
    
        #return the maximum result of changing and not changing
        best = max(no_change, change)
        return best
    
    
    L = [10, 4, 22, 8, 64]
    print func(L, 0)
    

    打印出来的

    93
    

    [10, 4, 22, 43, 64]的最终名单

    但请记住,这种方法效率低下,不适用于大量输入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      相关资源
      最近更新 更多