我正在尝试解决一个问题,即你会得到一个整数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
我试图用所有排列来解决这个问题,但是当输入约束很高时,它给了我超出时间限制的时间。我尝试使用动态编程,但2D数组存储的内存限制超过mem[150001][2001]
。我尝试在[150001] [2]中做了一些关于DP的教程,但我不能。任何帮助将不胜感激。
答案 0 :(得分:0)
我的解决方案运行得非常快。我没有做严格的计时或内存检查。我的解决方案是递归的,虽然我没有看到如何让它动态:
在步骤1中递归,从N中减去刚添加的数字
这为您提供了一个可能不完美的解决方案:
如果N = 18,数组= {12,9,8,5,4},你最终会得到子集答案{12,5}而不是{9,5,4}。您可以说此解决方案中的“差距”为gap = 1
。
对于子集的每个成员m
,您将再次解决,将N设置为m + gap
,将Array设置为原始Array的成员,不包括子集的所有成员。在我们的例子中,我们会产生另外两个问题:N = 13,Array = {9,8,4},N = 6,Array = {9,8,4}。
采用上一步提供的最佳解决方案,由间隙减少决定。如果最佳解决方案中的差距小于较大问题中的差距,则用子集替换目标数字。在我们的例子中,N = 13完全由{9,4}解决,其中12对象,所以我们用{9,4}代替12给我们{9,4,5}。
如果这个子问题gap=0
,我们就完成了。
gap=0
,但确实做了替换,请在步骤4中进行递归。我是用一个相当丑陋的C#来做的,不过如果你想要代码,我可以把它清理一下。
编辑:添加了代码
我试图将C#细节与特定功能联系起来。保持事物整个时间排序是没有必要的,我相信你可以减少ImproveOnGaps函数中的内存使用量。
运行:
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(),
}
}
答案 1 :(得分: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