【问题标题】:How many times I can partition the array into 2 parts with equal sum?我可以将数组分成总和相等的两部分多少次?
【发布时间】:2016-08-03 10:52:23
【问题描述】:

我想找到可以将数组划分为 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

我们不能分成两部分,总和相等。因此,她的最大可能得分为 0。

input2 4 1 0 1 1 0 1

output2 3

我的解决方案很慢。我们怎样才能更有效地解决它? 这个问题来自hackerrank

【问题讨论】:

  • 当您说split时,您的意思是与原始数组只能有一个分隔符吗?所以如果a = [ 1, 2, 3, 4, 5, 6 ],就不能像b = [ 2, 3, 4 ], c = [ 1, 5, 6 ]一样拆分?
  • 如果它有效 -> Codereview 它
  • 还有输入数组是什么样的?任意数字?
  • @HubertGrzeskowiak 最初,有一个数组 A ,其中包含 N 个整数。 N
  • @Fabjan 请阅读从After each successful partition ..开始的问题

标签: c# algorithm


【解决方案1】:
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))
}

那么,

int[] array;
var results = new List<Tuple<int[], int[]>>();

for (int i = 0; i < array.Length; i++) {
    var tuple = SplitIntArray(array, i);

    if (tuple.Item1.Sum() == tuple.Item2.Sum()) {
        results.Add(tuple);
    }            
}

// results contains all pairs of arrays that sum to the same value.
foreach (var tuple in results) {
    Console.WriteLine(
        String.Format("{0} == {1}", AggregateSumString(tuple.Item1), AggregateSumString(tuple.Item2)));
}

【讨论】:

  • 如果我们假设数字在一个范围内都是随机的并且随机分布,那么大多数解决方案将更多地位于输入数组的中间。我建议不要从 0 迭代,而是从 array.Length/2 双向迭代。
  • 赞成一个好的建议,但我已经设计了我的答案来涵盖数组总和的每个组合;如果它们是随机的,那么[ 4, 4, 0, 0, 0, 0, 4, 4 ] 并不意味着从array.Length / 2 开始更有效。同意平均而言,如果不必对每种组合进行测试,那将更有效。
  • firstArraysecondArray 是什么?
  • @ivan_petrushenko 对不起,错字
  • 根据聚合签名,sumString 也必须是 int,但不能是 string
【解决方案2】:

这是一个相当有效的方法:

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;
}

【讨论】:

  • 谢谢,我通过hackerrank运行了这段代码,但它只通过了一次测试
  • 嗯,它可能是什么。整数溢出可能。有什么说法吗?
  • 现在它通过了所有测试:)
猜你喜欢
  • 1970-01-01
  • 2015-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-11
  • 2012-01-07
  • 2015-03-11
相关资源
最近更新 更多