【问题标题】:Selecting Wine Dynamic Programming选择 Wine 动态规划
【发布时间】:2017-01-16 01:27:19
【问题描述】:

我最近开始学习动态编程,发现了以下问题:

想象一下,您在一个架子上放置了 N 种葡萄酒的集合。为简单起见,让我们分别用 1 到 N 的整数为葡萄酒站在货架上时从左到右编号。第i个酒的价格是pi(不同酒的价格可以不同)。

因为葡萄酒每年都在变好,假设今天是第 1 年,那么第 y 年第 i 种葡萄酒的价格将是 y*pi,即当年价值的 y 倍。

你想卖掉你所有的酒,但你想从今年开始每年只卖一种酒。还有一个限制 - 每年您只能出售货架上最左边或最右边的葡萄酒,并且不允许重新订购货架上的葡萄酒(即它们必须保持与开始时相同的顺序)。

您想知道,如果您以最佳顺序销售葡萄酒,您可以获得的最大利润是多少。

来源:https://www.quora.com/Are-there-any-good-resources-or-tutorials-for-dynamic-programming-besides-the-TopCoder-tutorial/answer/Michal-Danil%C3%A1k?srid=3Otg

答案是自上而下的方法,我想创建一种自下而上的方法。以下是我对问题的定义:

F(l,r) 是从指定的左右索引中挑选葡萄酒所产生的利润函数

输入:p 是葡萄酒价格的数组

F(l,r) = max(年 * p[l] + F(l+1,r) * (年+1), 年 * p[r] + F(l,r-1) * (year+1))

约束:l+r

我创建了以下 Python 代码来解决这个问题

def wine(Price):
    length = len(Price)
    DP = [[0] * (length+1) for _ in range(length+1)]

    for y in range(1,length+1): #Or can be range(length, 0, -1):
        for l in range(0, length):
            for r in range(length-1, -1, -1):
                if l+r <= length:
                    DP[l][r] = max(y * Price[l] + DP[l+1][r] * (y+1), \
                                    y * Price[r] + DP[l][r-1] * (y+1))
    return DP

我已将 Price 数组设置为 [2,3,5,1,4]。消息来源表明 Max Profit 是 50。但是,我无法用我编写的代码识别该值。有人可以帮助确定我的逻辑问题吗?

【问题讨论】:

    标签: python dynamic-programming


    【解决方案1】:

    您必须考虑以下几点:

    • 在这种方法中,我们必须处理对角线和下半部分 三角形将为零
    • 这里的年份最初必须是最大值,即 year = price.size()
    • 当前年份将使用公式计算(可以任意选择)。我用这个 公式:year = year - (j-i) //这对于处理上三角形很有用
    • 你必须正确使用 DP 数组
    int bottomUp(vector<int>price, int year, int start, int end) { int dp[100][100] = {0}; dp[start][start] = year*price[start]; for(int j=start+1; j<=end; j++) { for(int i=j; i>=0; i--) { int m = year-(j-i); int x = m*bottle[j] + dp[i][j-1]; int y = m*bottle[i] + dp[i+1][j]; dp[i][j] = max(x,y); } } return dp[start][end]; }

    这里的代码返回可能的最大值 但是,我还粘贴了 DP 数组外观的屏幕截图

    【讨论】:

    • 你是怎么得到年份公式的?可以分享一下资源吗?
    【解决方案2】:

    为了解决这个问题,我们需要以不同的顺序迭代数组。从左上角开始,我们按年份得到以下值:

    # l is the vertical axis
    y0 y1 y2 y3 y4
    y1 y2 y3 y4 
    y2 y3 y4
    y3 y4
    y4
    

    所以每年我们都需要在lr 上迭代一条对角线而不是双循环。所以,代码是:

    def wine(price):
        length = len(price)
        DP = [[0] * (length+1) for _ in range(length+1)]  # +1 for year0 in the corner
        for y in range(1,length+1):  # y1, y2... yN
            for x in range(y+1):  # intermediate values 0 to y
                l = x   # which is used to calculate the real l, r
                        # so, for the first year we get tuples (0, 1) and (1, 0)
                r = y - l  # we just go along the diagonal
                # magic with l/r > 0 is used to prevent unwanted negative indexes
                # so, False and price[-1] = False and max(False, 4) = 4
                DP[l][r] = max(l > 0 and DP[l-1][r] + y * price[l-1], \
                               r > 0 and DP[l][r-1]  + y * price[-r])
        return DP
    

    试运行:

    >>> pprint(wine([2,3,5,1,4]))
    [[0, 4, 6, 21, 33, 43],
     [2, 10, 13, 33, 48, 0],
     [8, 20, 25, 50, 0, 0],
     [23, 40, 50, 0, 0, 0],
     [27, 47, 0, 0, 0, 0],
     [47, 0, 0, 0, 0, 0]]
    

    【讨论】:

    • 非常感谢,快速提问:我们能否在不检查矩阵的情况下知道哪个值将具有最大值?
    • 当然。我们可以在循环之前添加变量(max_value=0)并在每次迭代结束时更新它(max_value = max(max_value, DP[l][r])。但是,我希望沿主对角线(即去年)检查值会更快:max(DP[-i][i] for i in range(len(price)+1))
    【解决方案3】:

    这里 i 和 j 分别表示给定 wines 数组的开始和结束索引。

    复发:

      max(wines[i] * year + maxProfit( i + 1, j, year + 1), 
           wines[j] * year + maxProfit( i, j - 1, year + 1));
    

    自上而下 DP

    int maxProfit_td(int *wines, int i, int j, int year, int dp[][100])
    {
        //  Base Case
        if (i > j)
            return 0;
    
        //Look Up
        if (dp[i][j] != 0)
            return dp[i][j];
        //  Recursive Case
        int op1 = wines[i] * year + maxProfit_td(wines, i + 1, j, year  + 1, dp);
        int op2 = wines[j] * year  + maxProfit_td(wines, i, j - 1, year  + 1, dp);
    
        return dp[i][j] = max(op1, op2);
    }
    

    自下而上 DP

    int maxProfit_bu(int *wines, int n)
    {
        int dp[100][100] = {0};
        // Wines array size
        int year = n;
        // Fill the diagonal elements i.e leaf nodes in case of recursion tree
        for (int i = 0; i < n; i++)
            dp[i][i] = year * wines[i];
    
        year--;
    
        // For remaining upper triangle
        for (int len = 2; len <= n; len++)
        {
            int start = 0, end = n - len;
            while (start <= end)
            {
                int cur = start + end - 1;
                dp[start][cur] = max(wines[start] * year + dp[start + 1][cur], 
                                      wines[cur] * year + dp[start][cur - 1]);
                start++;
            }
            year--;
        }
    
        return dp[0][n - 1];
    }
    
    

    函数调用

        int dp[100][100] = {0};
        cout << maxProfit_td(wines, 0, n - 1, 1, dp) << endl;
        cout << maxProfit_bu(wines, n) << endl;
    

    【讨论】:

    • 你能解释一下内部循环和它之前的变量吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    • 2016-02-09
    • 2020-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多