Line of wines

There are N wines in a row. Each year you sell either the leftmost or the rightmost wine. The i-th wine has initial price p[i] and price k * p[i] in the k-th year. What is the maximum possible total profit?

Solution 1. Bottom up dynamic programming

state:
dp[i][j]: the max possible profit of wines[i, j] at year i - 0 + N - 1 - j + 1 = N + i - j
dp[i][j] = max of {p[i] * (N + i - j) + dp[i + 1][j], p[j] * (N + i -j) + dp[i][j - 1]}

Initialization:
dp[i][i] = N * p[i]

Ans:
dp[0][N - 1]

Loop over i and j. In this case, the loop order matters. In order to compute dp[i][j], we need to have dp[i + 1][j] and dp[i][j - 1] first. This means we need to loop i in decreasing order from N - 1 to 0 and j in increasing order from i + 1 to N - 1.

    public int maxProfitBottomUp(int[] p) {
        int N = p.length;
        int[][] dp = new int[N][N];
        for(int i = 0; i < N; i++) {
            dp[i][i] = N * p[i];
        }
        for(int i = N - 1; i >= 0; i--) {
            for(int j = i + 1; j < N; j++) {
                dp[i][j] = Math.max(p[i] * (N + i - j) + dp[i + 1][j], p[j] * (N + i -j) + dp[i][j - 1]);
            }
        }
        return dp[0][N - 1];
    }

Loop over the interval length first, then loop over all possible left start.

    public int maxProfitBottomUp(int[] p) {
        int N = p.length;
        int[][] dp = new int[N][N];
        for(int i = 0; i < N; i++) {
            dp[i][i] = N * p[i];
        }
        for(int len = 2; len <= N; len++) {
            for(int l = 0; l <= N - len; l++) {
                int r = l + len - 1;
                int y = N + l - r;
                dp[l][r] = Math.max(p[l] * y + dp[l + 1][r], p[r] * y + dp[l][r - 1]);
            }
        }
        return dp[0][N - 1];
    }

Solution II. Top down dynamic programming

State:

dp[l][r]: the max possible profit before we go to interval [l, r].

dp[l][r] = Math.max(dp[l][r + 1] + p[r + 1] * (currY - 1), dp[l - 1][r] + p[l - 1] * (currY - 1)), currY = N + l - r.


Initialization:
dp[0][N - 1] = 0


Answer:
max of {dp[i][i] + p[i] * N}

    public int maxProfitTopDown(int[] p) {
        int N = p.length;
        int[][] dp = new int[N][N];
        dp[0][N - 1] = 0;

        for(int len = N - 1; len >= 1; len--) {
            for(int l = 0; l + len <= N; l++) {
                int r = l + len - 1;
                int currY = N + l - r;
                if(r < N - 1) {
                    dp[l][r] = Math.max(dp[l][r], dp[l][r + 1] + p[r + 1] * (currY - 1));
                }
                if(l > 0) {
                    dp[l][r] = Math.max(dp[l][r], dp[l - 1][r] + p[l - 1] * (currY - 1));
                }
            }
        }

        int res = 0;
        for(int i = 0; i < N; i++) {
            res = Math.max(res, dp[i][i] + p[i] * N);
        }
        return res;
    }

Solution III. Bottom up dynamic programming with prefix sum 

Key idea: if we assume every interval starts with year 1 and already have the max profit for interval[l, r - 1] and [l + 1, r], then to compute interval[l, r], we shift all the years for [l, r - 1] and [l + 1, r] by 1, then add either p[r] or p[l]. This is the prefixsum of interval[l, r].

dp[l][r]: the max possible profit of wines[l, r] starting from year 1.
dp[l][r] = Math.max(dp[l][r - 1], dp[l + 1][r]) + prefixSum[l, r]

init: dp[i][i] = 1 * p[i]

Ans: dp[0][N - 1]

    public int maxProfitPrefixSum(int[] p) {
        int N = p.length;
        int[][] dp = new int[N][N];
        for(int i = 0; i < N; i++) {
            dp[i][i] = p[i];
        }
        int[] prefixSum = new int[N];
        prefixSum[0] = p[0];
        for(int i = 1; i < N; i++) {
            prefixSum[i] = prefixSum[i - 1] + p[i];
        }
        for(int i = N - 1; i >= 0; i--) {
            for(int j = i + 1; j < N; j++) {
                dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]) + prefixSum[j] - (i > 0 ? prefixSum[i - 1] : 0);
            }
        }
        return dp[0][N - 1];
    }
原文地址:https://www.cnblogs.com/lz87/p/11518228.html