0

0 <= N <= 150000 の整数 N が与えられるという問題を解決しようとしています。また、配列の長さが最大 ​​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 に関するいくつかのチュートリアルで言及されているように、それを実行しようとしましたが、できませんでした。どんな助けでも大歓迎です。

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、配列 = {12, 9, 8, 5, 4} の場合、{9, 5, 4} ではなくサブセットの回答 {12, 5} になります。このソリューションの「ギャップ」は であると言えますgap = 1

  3. サブセットの各メンバーmについて、N を に設定しm + gap、Array をサブセットのすべてのメンバーを除く元の配列のメンバーに設定して、再度解決します。この例では、N = 13、配列 = {9, 8, 4}、および N = 6、配列 = {9, 8, 4} という 2 つの問題が発生します。

  4. ギャップ削減によって決定された、前のステップで提供された最適なソリューションを使用します。最適なソリューションのギャップがより大きな問題のギャップよりも小さい場合は、対象の数をサブセットに置き換えます。この場合、N = 13 は {9, 4} によって完全に解決され、12 を対象としているため、12 を {9, 4} に置き換えて {9, 4, 5} を取得します。

  5. このサブ問題の場合gap=0は、これで完了です。

  6. に達しないgap=0が置換を行った場合は、手順 4 を再帰します。
  7. 手順 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(),
    }


}
于 2013-06-06T17:43:32.887 に答える