如何将递归算法转换为动态规划?

问题描述

我有这个算法:

static int findMaxRec(int[] w,int[] v,int W,int n)
{
    int max = int.MinValue;
    int res;
    for (int i = 0; i < n; i++)
    {
        if (w[i] <= W)
        {
            if (w[i] == W)
                res = v[i]; // F(0) + v[i] = v[i]
            else
                res = findMaxRec(w,v,W - w[i],n) + v[i];

            max = max < res ? res : max;
        }
    }
    return max;
}

如何将其转换为动态规划算法? 我尝试了几个想法,但似乎没有一个可行。所以我被卡住了。

附言w 和 v 只是简单的数字数组,没有什么更好的了。 W只是一个数字。这个算法没有实现任何特定的任务,我只是在一本书中找到的,他们要求为给定的公式实现算法。

更新:

static int findMaxDyn(int[] F,int[] w,int n)
{
    int max = int.MinValue;
    int res;
    for (int i = 0; i < n; i++)
    {
        if (w[i] <= W)
        {
            if (F[W - w[i]] == int.MinValue) // calculate only if -inf
            {
                if (w[i] == W)
                    res = v[i]; // F(0) + v[i] = v[i]
                else
                    res = findMaxDyn(F,w,n) + v[i];

                max = max < res ? res : max;
                F[W - w[i]] = max;
            }
        }
    }
    return max;
}

这给出了与递归算法不匹配的错误答案。而且好像还在用递归……

我画的递归树

int [] w = new []{ 4,3,2,1};
int [] v = new []{ 4,1};    
int W = 4;
int n = 4;

recursion tree

解决方法

我仍然不知道算法要做什么,但非递归函数可能是:

   public static int findMaxRec_NonRecursive(int[] Vect_w,int[] Vect_v,int W,int n)
        {
           
            List<int> prevWValues = new List<int>();
            List<int> prevVValues = new List<int>();
            List<int> prevIndex_i = new List<int>();
            List<int> prevMaxValue = new List<int>();
            int ListIndex = 0,iniIndex = 0,max = int.MinValue;

            startOver:
            
            for (int i = iniIndex; i < n; i++)
            {
                if (Vect_w[i] <= W)
                {                   
                    if (Vect_w[i] == W)                        
                        max = Math.Max(Vect_v[i],max);
                    else
                    {
                        if (prevWValues.Count > ListIndex)
                        {
                            prevWValues[ListIndex] = W;
                            prevIndex_i[ListIndex] = i;
                            prevVValues[ListIndex] = Vect_v[i];
                            prevMaxValue[ListIndex] = max;
                        }
                        else
                        {
                            prevWValues.Add(W);
                            prevIndex_i.Add(i);
                            prevVValues.Add(Vect_v[i]);
                            prevMaxValue.Add(max);
                        }
                        W -= Vect_w[i];
                        ListIndex++;
                        iniIndex = 0;                       
                        max = int.MinValue;
                        goto startOver;
                    }                   
                }
            }

           
            if (ListIndex>0)
            {
                ListIndex--;
                iniIndex = prevIndex_i[ListIndex]+1;
                W = prevWValues[ListIndex];  
                max = Math.Max(max+ prevVValues[ListIndex],prevMaxValue[ListIndex]);
                goto startOver;
            }    
           
            return max;
        }

对于“gotos”很抱歉,我发现在这种情况下编程更容易。此外,我对您的输入变量进行了重命名,以免发疯。

编辑

正如其他人指出的那样,它可以用作背包算法,因此知道它的目的是做什么,您可以优化/简化更多(这类算法的复杂性随着 n 呈指数增长)。例如,您可以对输入的 Vect_W 值进行排序并用数组替换列表。

 public static int findMaxRec_NonRecursive(int[] Vect_w,int n)
        {
            Array.Sort(Vect_w,Vect_v);
            n = Math.Min(n,Vect_w.Length);

            //Remove here repeated elements in Vect_w selecting the one with higher Vect_v if uniqueness is not assured

            int minVectW = Vect_w[0];

            int L = W / minVectW + 1;
            int[] prevWValues = new int[L];
            int[] prevVValues = new int[L];
            int[] prevIndex_i = new int[L];
            int[] prevMaxValue = new int[L];
            int ListIndex = 0,iniIndex = n - 1,max = int.MinValue,PrevUsefullIndex = 0;            

            startOver:

            for (int i = iniIndex; i >= 0; i--)
            {                
                if (Vect_w[i] <= W)
                {
                    if (PrevUsefullIndex < i)
                        PrevUsefullIndex = i;

                    if (Vect_w[i] == W)
                        max = Math.Max(Vect_v[i],max);
                    else
                    {
                        int newW = W - Vect_w[i];
                        if (newW < minVectW)
                            max = Math.Max(Vect_v[i],max);
                        else
                        {
                            prevWValues[ListIndex] = W;
                            prevIndex_i[ListIndex] = i;
                            prevVValues[ListIndex] = Vect_v[i];
                            prevMaxValue[ListIndex] = max;

                            W = newW;                       
                            ListIndex++;
                            iniIndex = PrevUsefullIndex;
                            PrevUsefullIndex = 0;
                            max = int.MinValue;
                            goto startOver;
                        }
                    }
                }
            }

            if (ListIndex > 0)
            {
                ListIndex--;
                iniIndex = prevIndex_i[ListIndex] - 1;
                W = prevWValues[ListIndex];
                max = Math.Max(max + prevVValues[ListIndex],prevMaxValue[ListIndex]);
                goto startOver;
            }

            return max;
        }

编辑 2

我刚刚发现发布的初始递归算法条件不佳,例如在最佳分支是第一个分支的情况下。我认为它应该有一个额外的条件来避免这种情况:


   //[...]
   else
    {          
            int innerMax = findMaxRec(w,v,W - w[i],n);
            if (innerMax == int.MinValue)
                 innerMax = 0;
            res = innerMax + v[i];

     }

   //[...]

我还在非递归算法中添加了一个条件,它通过检查当新的 W 低于最小的 vect_W 元素时是否可以正式关闭分支来执行几乎相同的操作。