从整数数组中查找给定数字的最小和最大可能总和

问题描述 投票:0回答:1

有一个整数元素的有序数组和一个限制。对于给定的元素,有必要找到可以大于或等于 limit 的最小可能和最大可能总和。

最小和是大于或等于limit

的序列的最小和

最大和,是满足条件的序列之和,并给出最大的结果

条件:

  • 序列的元素可以重复:例如您可以取 101 + 101 + 101 + 101 + 101 + 201 + 201
  • 求和时,不能跳过数组的元素,例如不能一次性相加101 + 301,必须是101 + 201 + 301
  • 达到极限后立即停止求和

第一个例子:

arr = [100, 200, 300, 1000]

限制=1000

这里的最小值是1000,因为我们可以拿10乘以100来举例。

这里的最大值是 1900,因为我们可以取 100 + 200 + 300 + 300 + 1000。

第二个例子:

arr = [3, 10, 15]

限制=1000

这里的最小值是 1000,因为我们可以取 300 乘以 3 和 10 乘以 10。

这里的最大值是 1014,因为我们可以取 318 乘以 3 和 3 乘以 10 以及 2 乘以 15

我写了一种基于递归的算法和一种基于动态规划的算法。在某些情况下,第一个和第二个都会产生错误的结果

Python 示例:

from pprint import pprint
from typing import Tuple, List

import resource
import sys

resource.setrlimit(resource.RLIMIT_STACK, (0x10000000, resource.RLIM_INFINITY))
sys.setrecursionlimit(0x100000)


class CollectingRangeAnalyzer:
    def __init__(self):
        self.memo = {}

    def recursion_method(self, pools: List[int], target_cap: int) -> Tuple[float, float]:

        self._collect_helper(pools, target_cap, [], 0)

        if not self.memo:
            raise ValueError("No valid range found")

        max_cap = max(self.memo)
        min_cap = min(self.memo, key=lambda x: x if x >= target_cap else float("inf"))
        return max_cap, min_cap

    def _collect_helper(self, pools_, target_sum_, path, cur_sum):

        if cur_sum >= target_sum_:
            return

        if self.memo.get(cur_sum):
            return

        for i, v in enumerate(pools_):
            cur_sum += v
            path.append(v)

            self._collect_helper(pools_, target_sum_, path, cur_sum)

            self.memo[cur_sum] = True

            cur_sum -= v
            path.pop()

    @staticmethod
    def dynamic_method(arr, limit):
        table = []
        arr_size = len(arr)
        n_cols = limit // arr[0] + 1
        max_cap = float("-inf")
        min_cap = float("inf")

        for i in range(arr_size):
            table.append([])
            for j in range(n_cols):
                table[i].append(0)

        for i in range(arr_size):
            for j in range(n_cols):
                if i == 0 and arr[0] * (j + 1) <= limit + arr[i]:
                    table[i][j] = arr[i] * (j + 1)
                elif i > j or j < 0:
                    table[i][j] = table[i - 1][j]
                else:
                    diagonal_prev = table[i - 1][j - 1]
                    j_prev = table[i][j-1]

                    if diagonal_prev < limit:
                        table[i][j] = diagonal_prev + arr[i]
                    else:
                        table[i][j] = max(diagonal_prev, j_prev)

                max_cap = max(table[i][j], max_cap)
                min_cap = min(table[i][j], min_cap, key=lambda x: x if x >= limit else float("inf"))

        return max_cap, min_cap

# First Example
first_analysis_class = CollectingRangeAnalyzer()

first_array = [100, 200, 300, 1000]
first_limit = 1000

rec_first = first_analysis_class.recursion_method(first_array, first_limit)     # output: (1900, 1000) SUCCESS
dynamic_first = first_analysis_class.dynamic_method(first_array, first_limit)   # output: (1900, 1000) SUCCESS

# But if added the 10000 in first_array and run again I'll get the wrong result in the recursion function.
#
# first_array = [100, 200, 300, 1000, 10000]
#
#
# rec_first = first_analysis_class.recursion_method(first_array, first_limit)     # output: (10900, 1000) WRONG
# dynamic_first = first_analysis_class.dynamic_method(first_array, first_limit)   # output: (1900, 1000) SUCCESS


# Second Example
second_analysis_class = CollectingRangeAnalyzer()

second_array = [3, 10, 15]
second_limit = 1000

rec_second = second_analysis_class.recursion_method(second_array, second_limit)    # output: (1014, 1000) SUCCESS
dynamic_second = second_analysis_class.dynamic_method(second_array, second_limit)  # output: (1012, 1000) WRONG


python algorithm function recursion dynamic
1个回答
0
投票

我认为您需要始终从序列的开头开始。你的例子仍然不清楚。如果这是真的,那么这应该有效:

import heapq

def min_max_limit_sum (arr, limit):
    # A path will be (idx_k, ...(idx_1, (idx_0, None))...)

    # By (value, index): path
    known = {}

    # heap of (value, counter, (next_index, prev_path))
    upcoming = [(0, 0, (0, None))]
    counter = 1

    best_min = limit + sum(arr)
    min_path = None
    best_max = limit - 1
    max_path = None
    # While there is stuff in the heap.
    while len(upcoming):
        value, _, next_path = heapq.heappop(upcoming)
        i = next_path[0]
        if (value, i) in known or len(arr) <= i:
            # Filter out alternate routes, and running off the end of the sequence.
            continue
        else:
            path = next_path[1]
            known[(value, i)] = path
            if value < limit:
                value += arr[i]
                heapq.heappush(upcoming, (value, counter, (i, next_path)))
                heapq.heappush(upcoming, (value, counter, (i+1, next_path)))
                counter += 1
            else:
                if value < best_min:
                    best_min = value
                    min_path = path
                if best_max < value:
                    best_max = value
                    max_path = path
    return (best_min, min_path, best_max, max_path)

print(min_max_limit_sum([100, 200, 300, 1000], 1000))

如果假设不成立,只需设置

upcoming
为每个索引都有一个条目。

© www.soinside.com 2019 - 2024. All rights reserved.