鉴于可以改变数字的符号,找到数组的最小总和

时间:2013-01-11 05:41:33

标签: arrays dynamic-programming

我在编码竞赛中遇到了这个问题 -

您将获得一个正整数数组,并且只要您需要,就可以更改任何整数的符号。 编写程序来计算该数组的最小总和。该总和应该> = 0。 例如 : 当我们改变1和5的符号时,数组= {1,2,4,5}然后sum = 0 {-1,2,4,-5}

我对此问题的解决方案是对数组进行排序并找到所有成员的总和。然后,我将从最大数字开始迭代地减少2 *(排序数组值) - 直到sum变为0或直到它变为负数。

但我的解决方案是错误的。拿12,13,14,15,16,50。我的代码将50改为-50并停止(即最小总和= 20)。但答案应该是12,-13,-14,-15,-16,50(最小值= 4)

4 个答案:

答案 0 :(得分:8)

这个问题可以改成一个傻瓜问题

考虑这个问题:

你得到n个整数,显然,你可以计算这些数的总和,假设它是S

您现在需要从中选择一组数字,并且旨在将这些选定的数字加起来尽可能接近S / 2

这可以使用DP算法来完成,这与knap-sack问题非常相似

你现在能做到吗? :)

这篇文章只是一个提示,如果你需要更多帮助,我可以提供更多细节

答案 1 :(得分:2)

我写了上面的代码,似乎工作正常。

如果我错了,请纠正我!

public int minimumSum(int[] array)
{
      int counter1, counter2, minimumSum;
      int n = array.length;
      counter1 = array[n-1];
      counter2 = array[n-2];
      // It is assumed that the array is sorted.
      int i = n-3;
      while(i>=0)
      {
          if(counter1 > counter2)
          { 
              counter2 = counter2 + array[i];
          }
          else
          {
              counter1 = counter1 + array[i];
          }
          i--;
      }
      if(counter1 > counter2)
      {
          minimumSum = counter1 - counter2;
      }
      else
          minimumSum = counter2 - counter1;
      return minimumSum ;
}

答案 2 :(得分:2)

我对其他答案感到恼火,说了一些关于傻瓜的事情。

它不像Knap-Sack。

Knap-Sack表示您有一个目标S和一个权重列表A,并且您正在寻找总计为A子集 S

你的问题要容易得多,因为你知道你必须拿走所有的数字而只是改变某些数字的标志。

因此,您可以将其视为两个堆栈,并尝试最小化它们的高度差异。这是离线调度问题,可以通过greedyalgorithm解决。有关代码示例,请参阅Augustus' answer

  • 对列表进行排序(最大值为分钟)
  • 对于每个对象,将其放在具有最低高度的堆栈上

你可以通过将较小堆栈上的所有对象的符号变为负数来求解你的解决方案。 (即计算两个堆栈的绝对差值)

答案 3 :(得分:1)

排序在这里不起作用,因为这不能通过贪婪的方法解决,就像在0-1背包中一样。您可以这样思考,数组中的每个元素都有2个选项,可以是负数也可以是正数。您可以通过选择一个数字(一个分支)或不选择它(另一个分支)来开发递归解决方案

这是我所说的实现。代码可以通过多种方式进行改进。对不起,如果它的评论非常少。我的时间不够。

#include "iostream"
#include <algorithm>
using namespace std;

int *arr,*flag,mini=1000; int* sol; //Flag array is used to see which all elements are selected in that call of the function

void find_difference(int* arr,int* flag,int n,int current,int *sol)
{
if(current==n)return;

int sum0=0, sum1=0,entered=0;
flag[current]=1;              //Selecting the current indexed number
for (int i = 0; i < n; ++i)
{
    if(flag[i]==0)
    {
        sum0=sum0+arr[i];
    }
    else
    {
        sum1=sum1+arr[i];
    }
}
    if(abs(sum0-sum1)<mini)
    {
        mini=abs(sum0-sum1);
        for (int j = 0; j < n; ++j)
        {
            sol[j]=flag[j];    //Remebering the optimal solution
        }
    }
find_difference(arr,flag,n,current+1,sol);  //Moving to the next index to perform the same operation (selecting or not selecting it)
flag[current]=0;                 // Not selecting it
for (int i = 0; i < n; ++i)
{
    if(flag[i]==0)
    {
        sum0=sum0+arr[i];
    }
    else
    {
        sum1=sum1+arr[i];
    }
}
    if(abs(sum0-sum1) < mini)
    {
        mini=abs(sum0-sum1);
        for (int j = 0; j < n; ++j)
        {
            sol[j]=flag[j];
        }
    }
find_difference(arr,flag,n,current+1,sol);
}

int main(int argc, char const *argv[])
{
int n;
cout<<"Enter size: ";
cin>>n;
cout<<"Enter the numbers: "
arr= new int[n];
flag= new int[n];
sol= new int[n];
for (int i = 0; i < n; ++i)
{
    cin>>arr[i];
    flag[i]=0;
    sol[i]=0;
}

find_difference(arr,flag,n,0,sol);

cout<<"Min = "<<mini<<endl;
cout<<"One set is: ";
for (int i = 0; i < n; ++i)
{
    if (sol[i]==1)
    {
        cout<<arr[i]<<" ";
    }
}
cout<<"\nOther set is: ";
for (int i = 0; i < n; ++i)
{
    if (sol[i]==0)
    {
        cout<<arr[i]<<" ";
    }
}
cout<<"\n";
return 0;
}