我有多少次可以将数组分成两部分并且总和相等?

时间:2016-08-03 10:52:24

标签: c# algorithm

我想多次发现我可以将数组划分为2个非空部分,这样左边分区中元素的总和等于右边分区中元素的总和。

每次成功分区后,我们会丢弃左侧分区或右侧分区,并使用剩余分区作为阵列继续播放。

最初,有一个数组a,包含N个整数。 N <= 2^14

static int Compute(int[] a, int arraySize)
{
     return ComputeNumberOfPartition(a.ToList(), arraySize,  0);
}

static int ComputeNumberOfPartition(List<int> a, int N, int points)
{
     List<int> left = new List<int>();
     List<int> right = new List<int>();

     for (int i = 0; i < N; ++i)
     {
         if (sum(left) <= sum(right)) { left.Add(a[i]);  }
         else                         { right.Add(a[i]); }
     }

     if (sum(left) == sum(right))
     {
         return left.Count >= right.Count ? 
                ComputeNumberOfPartition(left, left.Count, ++points) :
                ComputeNumberOfPartition(right, right.Count, ++points);
     }

     return points;
}

static int sum(List<int> a)
{
     int sum = 0;
     for (int i = 0; i < a.Count; ++i)
     {
         sum += a[i];
     }

     return sum;
}

例如:

input1 3 3 3

output1 0

我们不能分成具有相等总和的2个部分。因此,她的最高分数为0。

input2 4 1 0 1 1 0 1

output2 3

我的解决方案很慢。我们如何更有效地解决它? 来自hackerrank

的此问题

2 个答案:

答案 0 :(得分:1)

{
  "_index" : "test",
  "_type" : "test",
  "_id" : "1212",
  "_version" : 7,
  "found" : true,
  "_source" : {
    "count" : 41,
    "list_data" : [ {
      "list_id" : 11,
      "timestamp" : 1469125397
    }, {
      "list_id" : 121,
      "timestamp" : 1469050965
    }]
  }
}

然后,

public Tuple<int[], int[]> SplitIntArray(int[] array, int index) {
    return new Tuple<int[], int[]>(
        array.Take(index).ToArray(),
        array.Skip(index).ToArray()
    );
}
public string AggregateSumString(int[] array) {
    return array.Select(i => i.ToString()).Aggregate((sumString, next) => 
        String.IsNullOrEmpty(sumString) ? next : String.Format("{0} + {1}", sumString, next))
}

答案 1 :(得分:0)

这是一种非常有效的方式:

static int CalcScore(int[] a)
{
    return CalcScore(a, 0, a.Length - 1, a.Sum(n => (long)n));
}

static int CalcScore(int[] a, int startPos, int endPos, long totalSum)
{
    long leftSum = 0;
    long rightSum = totalSum;
    for (int splitPos = startPos; splitPos < endPos; splitPos++)
    {
        leftSum += a[splitPos];
        rightSum -= a[splitPos];
        if (leftSum > rightSum) break;
        if (leftSum == rightSum)
        {
            int leftScore = CalcScore(a, startPos, splitPos, leftSum);
            int rightScore = CalcScore(a, splitPos + 1, endPos, rightSum);
            return 1 + Math.Max(leftScore, rightScore);
        }
    }
    return 0;
}

主要算法是第二种递归方法。首先,它使用预先计算的总和。其次,它使用由startPosendPos定义的包容性数组索引范围,这允许我们避免内存分配。然后它简单地从开始到结束迭代 - 1将当前值加到左和,并从右和减去它,直到左和大于右和,在这种情况下它提前退出(这是基于约束,值不是负数),或者如果两个总和相等,它会对分割进行计数,并递归地添加左右部分的最大分割。

更新:考虑到这样一个事实,即只有当总和可以被2整除时,数组范围才能在具有相等和的两个部分上分割(因此目标左右总和应该是总和的一半),递归部分可以进一步优化如下:

static int CalcScore(int[] a, int startPos, int endPos, long totalSum)
{
    long halfSum = totalSum / 2;
    if (halfSum + halfSum != totalSum) return 0;
    long leftSum = 0;
    for (int splitPos = startPos; splitPos < endPos; splitPos++)
    {
        leftSum += a[splitPos];
        if (leftSum > halfSum) break;
        if (leftSum == halfSum)
        {
            int leftScore = CalcScore(a, startPos, splitPos, halfSum);
            int rightScore = CalcScore(a, splitPos + 1, endPos, halfSum);
            return 1 + Math.Max(leftScore, rightScore);
        }
    }
    return 0;
}