0

我正在尝试解决一个问题,即您将获得一个整数 N,它是 0 <= N <= 150000。您还将获得一个包含整数的数组,数组长度最大为 2000。

我想得到最接近 N 或完全等于 N 的数组子集的总和。问题指出总和应该完全等于 N,但是如果没有子集可以完全达到 N,那么我们应该带来最接近但小于 N。例如:

N = 11,Array = { 2 , 3 , 5 , 7 }在这种情况下,输出应该是 10
N = 12,Array = { 4 , 6 , 9 }在这种情况下,输出应该是 10
N = 10,Array = { 2 , 3 , 3 , 10 }在这种情况下,输出应该是 10

我试图用所有排列来解决这个问题,但它给了我时间限制,因为输入约束很高。我尝试使用动态编程,但二维数组存储给出的内存限制超过了mem[150001][2001]. 我尝试在 [150001][2] 中这样做,因为提到了一些关于 DP 的教程,但我做不到。任何帮助,将不胜感激。

4

2 回答 2

0

在 WumpusQ 发布的链接的帮助下,我想我得到了一些有用的东西。基本上我使用链接中的 DP 方法,然后从 N 开始向后查找有效总和并返回遇到的第一个。(在 Python 中)

from collections import defaultdict

def dpFunc(N, Array):
  # determine range of possible values
  minSum = reduce(lambda x, y: x+y, [x for x in Array if x < 0], 0)
  maxSum = reduce(lambda x, y: x+y, [x for x in Array if x > 0], 0)
  # Initialize
  Q = defaultdict(lambda: False)
  for s in xrange(minSum, maxSum + 1):
    Q[(0,s)] = (Array[0] == s)
    for i in xrange(1, len(Array)):
      Q[(i,s)] = Q[(i-1,s)] or (Array[i] == s) \
          or Q[(i-1,s-Array[i])]
  for s in xrange(N, minSum -1, -1):
    if (Q[(len(Array)-1,s)]):
      return s
于 2013-06-07T00:00:11.020 回答
0

我有一个运行很快的解决方案。不过,我没有进行严格的时间或内存检查。我的解决方案是递归的,尽管我不知道如何使它成为动态的:

  1. 在数组中找到小于 N 的最大数,将其添加到子集中
  2. 递归第 1 步,从 N 中减去刚刚添加的数字

    这为您提供了一个可能不完美的解决方案: 如果 N = 18, Array = {12, 9, 8, 5, 4},您将得到子集答案 {12, 5} 而不是 {9, 5, 4}。你可以说这个解决方案中的“差距”是gap = 1.

  3. 对于子集的每个成员m,您将再次求解,将 N 设置为m + gap,并将 Array 设置为原始 Array 的成员,不包括子集的所有成员。在我们的示例中,我们将产生另外两个问题:N = 13, Array = {9, 8, 4} 和 N = 6, Array = {9, 8, 4}。

  4. 采用上一步提供的最佳解决方案,由差距缩小确定。如果最佳解决方案中的差距小于较大问题中的差距,则将目标数字替换为子集。在我们的例子中,N = 13 可以通过以 12 为目标的 {9, 4} 完美解决,因此我们将 12 替换为 {9, 4},从而得到 {9, 4, 5}。

  5. 如果gap=0对于这个子问题,我们就完成了。

  6. 如果您没有达到gap=0,但确实做了替换,请递归第 4 步。
  7. 如果您没有在第 4 步中进行更换,那么您就有了最好的解决方案,您就完成了。

我在一个相当丑陋的 C# 中完成了它,但如果你想要代码,我可以稍微清理一下。

编辑:添加代码

我试图将 C# 细节与特定功能隔离开来。没有必要一直对事物进行排序,而且我相信您可以在 ImprovementOnGaps 函数中减少内存使用量。

跑步:

void Main()
{
    Problem p = Solvers.GenerateRandomProblem();
    Solution imperfectSolution = Solvers.SolveRecursively(p);
    Solution bestPossibleSolution = Solvers.ImproveOnGaps(s);
}


class Solution
{
    public Problem Problem;
    public int[] NumbersUsed;
    public int n;
    public int[] NumbersUnused;
}

class Problem
{
    public int N;
    public int[] Array;
}

class Solvers
{
    public static Problem GenerateRandomProblem()
    {
        Random r = new Random();
        int N = r.Next(1500000);
        int arraySize = r.Next(1, 2000);

        int[] array = new int[arraySize];
        for(int i = 0; i < arraySize; i++)
            array[i] = r.Next(1, 15000);

        Problem problem = new Problem
        {
            N = N,
            Array = array
        };

        return problem;
    }

    public static Solution SolveRecursively(Problem p)
    {
        return SolveRecursively( new Solution
        {
            Problem = p,
            n = 0,
            NumbersUnused = SortAscending(p.Array),
            NumbersUsed = new int[0]
        });
    }

    private static Solution SolveRecursively(Solution s)
    {
        if(s.n == s.Problem.N)
            return s;

        for(int i = s.NumbersUnused.Length - 1; i >= 0; i--) //
        {
            if(s.n + s.NumbersUnused[i] <= s.Problem.N)
            {
                return SolveRecursively(new Solution
                {
                    n = s.n + s.NumbersUnused[i],
                    NumbersUnused = SkipIthPosition(s.NumbersUnused, i),
                    NumbersUsed =  AddToSortedArray(s.NumbersUsed, s.NumbersUnused[i]),
                    Problem = s.Problem
                });
            }
        }
        return s;
    }

    public static Solution ImproveOnGaps(Solution s)
    {
        if(s.n == s.Problem.N)
            return s;

        int gap = s.Problem.N - s.n;
        List<Problem> newProblems = new List<Problem>();
        foreach (int i in s.NumbersUsed)
        {
            newProblems.Add(new Problem
            {
                Array = s.NumbersUnused,
                N = i + gap
            });
        }

        int newGap = gap;
        Solution bestImprovement = null;
        foreach (Problem p in newProblems)
        {
            Solution tempSolution = SolveRecursively(p);
            if(tempSolution.Problem.N - tempSolution.n < newGap)
                bestImprovement = tempSolution;
        }

        if(bestImprovement != null)
        {
            List<int> usedNumbers = s.NumbersUsed.ToList();
            usedNumbers.Remove(bestImprovement.Problem.N - gap);
            usedNumbers.AddRange(bestImprovement.NumbersUsed);

            List<int> unusedNumbers = s.NumbersUnused.ToList();
            foreach (int i in bestImprovement.NumbersUsed)
                unusedNumbers.Remove(i);

            return ImproveOnGaps(new Solution
            {
                n = usedNumbers.Sum(),
                NumbersUnused = unusedNumbers.ToArray(),
                NumbersUsed = usedNumbers.ToArray(),
                Problem = s.Problem
            });
        }

        return s;

    }

    private static int[] SortAscending(int[] array)
    {
        return array.OrderBy(i => i).ToArray();
    }

    private static int[] SkipIthPosition(int[] array, int i)
    {
        return array.Take(i)
            .Union(array.Skip(i + 1).Take(array.Length - 1 - i))
            .ToArray();
    }

    private static int[] AddToSortedArray(int[] array, int i)
    {
        return array.Concat(new int[] { i }).OrderBy(d => d).ToArray(),
    }


}
于 2013-06-06T17:43:32.887 回答