给定一个正元素数组,您必须翻转其某些元素的符号,以使数组元素的结果总和应为最小非负数(尽可能接近零)。返回最小编号需要翻转其符号的元素,以使结果总和为最小非负数。请注意,所有数组元素的总和将不超过10 4 。
例子:
Input: arr[] = {15, 10, 6}
Output: 1
Here, we will flip the sign of 15
and the resultant sum will be 1.
Input: arr[] = [14, 10, 4]
Output: 1
Here, we will flip the sign of 14 and the resultant sum will be 0.
Note that flipping the signs of 10 and 4 also gives
the resultant sum 0 but the count of flipped elements is not minimum.
天真的方法:对于数组中每个元素A [i],0≤i 因此,我们总共可以配置2 n个阵列。我们可以维持每种配置中元素的总和和翻转的数量,并跟踪最小总和(领带被最小翻转数打破)。最小总和配置中的翻转数将是答案。 If sign of ar[i – 1] is not flipped to make sum = j 注意:由于数组元素的总和在翻转后可能为负。因此,我们不能使用2D数组进行制表,因为在dp [i] [j]中,j是总和,数组的索引不能为负。因此,我们将使用哈希表数组。数组的大小为n + 1。 下面是上述方法的实现: 时间复杂度: O(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] = dp[i – 1][j – A[i – 1]]
If sign of ar[i – 1] is flipped to make sum = j
dp[i][j] = min(dp[i][j], dp[i – 1][j + A[i – 1]]+1)
重叠的子问题:就像0/1背包问题一样,这里也存在重叠的子问题。我们不需要一次又一次地评估结果,而是可以将子问题的结果存储在表中。
时间复杂度: O(n *总和)
辅助空间: O(n *和)
其中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。
以下是我们需要优化空间的更改。
C++
// C++ implementation of the approach
#include
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
3
辅助空间: O(sum),其中n是元素个数,sum是不翻转的数组元素之和。