📌  相关文章
📜  翻转数组元素的最小符号以获得可能的正元素的最小总和

📅  最后修改于: 2021-10-27 08:32:38             🧑  作者: Mango

给定一个正元素数组,您必须翻转其中一些元素的符号,以便数组元素的总和应该是最小的非负数(尽可能接近零)。返回最小编号。需要翻转符号的元素的数量,以便结果和是最小的非负数。请注意,所有数组元素的总和不会超过 10 4
例子:

朴素的方法:对于数组的每个元素 A[i], 0 ≤ i < n,我们有 2 个选项。

  1. A[i] 的符号被翻转(-ve)。
  2. A[i] 的符号没有翻转(+ve)。

所以我们总共可以有 2 n 种可能的阵列配置。我们可以维护每个配置中元素的总和和翻转次数,并跟踪最小和(平局被最小翻转次数打破)。最小和配置中的翻转次数将是答案。
时间复杂度: O(2 n ),其中 n 是数组中的元素数。
高效方法:这个问题可以使用动态规划解决,是标准 0/1 背包问题的变体。不同之处在于我们有 2 个选项,即要么在背包中包含一个项目,要么排除它,这里就像是否翻转元素的符号。不是背包问题中的袋重,这里是没有翻转的数组所有元素的总和(问题陈述中给出的最大值为 10 4 )。
最优子结构:令 dp[i][j] 是数组的前 i 个元素中所需的最小翻转次数,以使总和等于 j。
1 ≤ i ≤ n 和 -sum ≤ j ≤ sum,其中 sum 是没有翻转的数组所有元素的总和。

注意:由于数组元素的总和在翻转后可能为负。所以我们不能使用二维数组进行制表,因为在 dp[i][j] 中,j 是数组的和,索引不能为负。因此,我们将使用哈希映射数组。数组的大小将为 n + 1。
重叠子问题:就像 0/1 背包问题一样,这里也有重叠子问题。我们不需要一次又一次地评估结果,而是可以将子问题的结果存储在表中。
时间复杂度: O(n * sum)
辅助空间: O(n * sum)
其中 n 是元素的数量,sum 是没有翻转的数组元素的总和。
空间优化:如果仔细观察最优子结构,dp[i][j] 将仅取决于 dp[i – 1][j – A[i – 1]]/dp[i – 1][j] + A[i – 1]]。因此,只涉及 2 行 i 和 i – 1。因此,我们只需要 2 行而不是 n + 1。
以下是我们优化空间所需的更改。

  1. 而不是采用数组大小=n+1 声明大小为 2 的数组。
  2. 引入一个布尔变量(比如标志)来在地图之间切换。我们可以初始化 dp[0] 地图并开始填充 dp[1]。在下一次迭代中,dp[0] 是当前地图,dp[1] 是之前的地图。通过这种方式,我们可以继续在 2 张地图之间切换。

下面是上述方法的实现:

C++
// C++ implementation of the approach
#include 
using namespace std;
 
// Function to return the
// minimum number of elements
// whose sign must be flipped
// to get the positive
// sum of array elements as close
// to 0 as possible
int solve(int A[], int n)
{
 
    // Array of unordered_map of size=2.
    unordered_map dp[2];
 
    // boolean variable used for
    // toggling between maps
    bool flag = 1;
 
    // Calculate the sum of all
    // elements of the array
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += A[i];
 
    // Initializing first map(dp[0])
    // with INT_MAX because
    // for i=0, there are no elements
    // in the array to flip
    for (int i = -sum; i <= sum; i++)
        dp[0][i] = INT_MAX;
 
    // Base Case
    dp[0][0] = 0;
 
    for (int i = 1; i <= n; i++) {
        for (int j = -sum; j <= sum; j++) {
            dp[flag][j] = INT_MAX;
            if (j - A[i - 1] <= sum && j - A[i - 1] >= -sum)
                dp[flag][j] = dp[flag ^ 1][j - A[i - 1]];
            if (j + A[i - 1] <= sum && j + A[i - 1] >= -sum
                && dp[flag ^ 1][j + A[i - 1]] != INT_MAX)
                dp[flag][j]
                    = min(dp[flag][j],
                          dp[flag ^ 1][j + A[i - 1]] + 1);
        }
 
        // For toggling
        flag = flag ^ 1;
    }
 
    // Required sum is minimum non-negative
    // So, we iterate from i=0 to sum and find
    // the first i where dp[flag ^ 1][i] != INT_MAX
    for (int i = 0; i <= sum; i++) {
        if (dp[flag ^ 1][i] != INT_MAX)
            return dp[flag ^ 1][i];
    }
 
    // In worst case we will flip max n-1 elements
    return n - 1;
}
 
// Driver code
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    cout << solve(arr, n);
 
    return 0;
}


Java
// Java implementation of
// the above approach:
class GFG {
 
    // Function to return the
    // minimum number of elements
    // whose sign must be flipped
    // to get the positive
    // sum of array elements as close
    // to 0 as possible
    public static int solve(int[] A, int n)
    {
        int[][] dp = new int[2000][2000];
 
        // boolean variable used for
        // toggling between maps
        int flag = 1;
 
        // Calculate the sum of all
        // elements of the array
        int sum = 0;
        for (int i = 0; i < n; i++)
            sum += A[i];
 
        // Initializing first map(dp[0])
        // with INT_MAX because for i=0,
        // there are no elements in the
        // array to flip
        for (int i = -sum; i <= sum; i++) {
            try {
                dp[0][i] = Integer.MAX_VALUE;
            }
            catch (Exception e) {
            }
        }
 
        // Base Case
        dp[0][0] = 0;
 
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {
                try {
                    dp[flag][j] = Integer.MAX_VALUE;
                    if (j - A[i - 1] <= sum
                        && j - A[i - 1] >= -sum)
                        dp[flag][j]
                            = dp[flag ^ 1][j - A[i - 1]];
                    if (j + A[i - 1] <= sum
                        && j + A[i - 1] >= -sum
                        && dp[flag ^ 1][j + A[i - 1]]
                               != Integer.MAX_VALUE)
                        dp[flag][j] = Math.min(
                            dp[flag][j],
                            dp[flag ^ 1][j + A[i - 1]] + 1);
                }
                catch (Exception e) {
                }
            }
 
            // For toggling
            flag = flag ^ 1;
        }
 
        // Required sum is minimum non-negative
        // So, we iterate from i=0 to sum and find
        // the first i where dp[flag ^ 1][i] != INT_MAX
        for (int i = 0; i <= sum; i++) {
            if (dp[flag ^ 1][i] != Integer.MAX_VALUE)
                return dp[flag ^ 1][i];
        }
 
        // In worst case we will flip max n-1 elements
        return n - 1;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.length;
        System.out.println(solve(arr, n));
    }
}
 
// This code is contributed by sanjeev2552


Python3
# Python3 implementation of the approach
 
# Function to return the minimum
# number of elements
# whose sign must be flipped
# to get the positive
# sum of array elements as close
# to 0 as possible
def solve(A, n):
 
    dp = [[0 for i in range(2000)] for i in range(2000)]
 
    # boolean variable used for
    # toggling between maps
    flag = 1
 
    # Calculate the sum of all
    # elements of the array
    sum = 0
    for i in range(n):
        sum += A[i]
 
    # Initializing first map(dp[0])
    # with INT_MAX because
    # for i=0, there are no elements
    # in the array to flip
    for i in range(-sum, sum+1):
        dp[0][i] = 10**9
 
    # Base Case
    dp[0][0] = 0
 
    for i in range(1, n+1):
        for j in range(-sum, sum+1):
            dp[flag][j] = 10**9
            if (j - A[i - 1] <= sum and j - A[i - 1] >= -sum):
                dp[flag][j] = dp[flag ^ 1][j - A[i - 1]]
            if (j + A[i - 1] <= sum
                and j + A[i - 1] >= -sum
                    and dp[flag ^ 1][j + A[i - 1]] != 10**9):
                dp[flag][j] = min(dp[flag][j],
                                  dp[flag ^ 1][j + A[i - 1]] + 1)
 
        # For toggling
        flag = flag ^ 1
 
    # Required sum is minimum non-negative
    # So, we iterate from i=0 to sum and find
    # the first i where dp[flag ^ 1][i] != INT_MAX
    for i in range(sum+1):
        if (dp[flag ^ 1][i] != 10**9):
            return dp[flag ^ 1][i]
 
    # In worst case we will flip max n-1 elements
    return n - 1
 
 
# Driver code
arr = [10, 22, 9, 33, 21, 50, 41, 60]
n = len(arr)
 
print(solve(arr, n))
 
# This code is contributed by mohit kumar 29


C#
// C# implementation of the above approach:
using System;
 
class GFG
{
 
// Function to return the minimum number
// of elements whose sign must be flipped
// to get the positive sum of array elements
// as close to 0 as possible
public static int solve(int[] A, int n)
{
    int[,] dp = new int[2000, 2000];
 
    // boolean variable used for
    // toggling between maps
    int flag = 1;
 
    // Calculate the sum of all elements
    // of the array
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += A[i];
 
    // Initializing first map(dp[0]) with
    // INT_MAX because for i=0, there are
    // no elements in the array to flip
    for (int i = -sum; i <= sum; i++)
    {
        try
        {
            dp[0, i] = int.MaxValue;
        }
        catch (Exception e){}
    }
 
    // Base Case
    dp[0, 0] = 0;
 
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= sum; j++)
        {
            try
            {
                dp[flag, j] = int.MaxValue;
                if (j - A[i - 1] <= sum &&
                    j - A[i - 1] >= -sum)
                    dp[flag, j] = dp[flag ^ 1,
                                     j - A[i - 1]];
                if (j + A[i - 1] <= sum &&
                    j + A[i - 1] >= -sum &&
                    dp[flag ^ 1,
                       j + A[i - 1]] != int.MaxValue)
                    dp[flag, j] = Math.Min(dp[flag, j],
                                           dp[flag ^ 1,
                                           j + A[i - 1]] + 1);
            } catch (Exception e) {}
        }
 
        // For toggling
        flag = flag ^ 1;
    }
 
    // Required sum is minimum non-negative
    // So, we iterate from i=0 to sum and find
    // the first i where dp[flag ^ 1,i] != INT_MAX
    for (int i = 0; i <= sum; i++)
    {
        if (dp[flag ^ 1, i] != int.MaxValue)
            return dp[flag ^ 1, i];
    }
     
    // In worst case we will flip
    // max n-1 elements
    return n - 1;
}
 
// Driver code
public static void Main(String[] args)
{
    int[] arr = { 10, 22, 9, 33,
                  21, 50, 41, 60 };
    int n = arr.Length;
    Console.WriteLine(solve(arr, n));
}
}
 
// This code is contributed by PrinciRaj1992


Javascript


输出:
3

时间复杂度: O(n * sum)。
辅助空间: O(sum) 其中 n 是元素数,sum 是没有翻转的数组元素的总和。

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程