动态规划:(一)线性 DP —— 3. 状态机模型

打家劫舍

解法一:线性 DP

分析

状态表示

  • q(i) 表示第 i 家的现金
  • f(i) 表示抢劫到第 i 家时的最大现金

状态转移

  • 限制条件:不能联系抢劫两家相邻的
  • 抢劫第 i - 2 家时,能抢第 i
  • 抢劫第 i - 1 家时,不能抢第 i

状态计算:最大值 max

代码

class Solution {
    public int rob(int[] q) {
        int n = q.length;
        int[] f = new int[n]; 
        
        f[0] = q[0];
        if (n == 1) return f[0];
        f[1] = Math.max(f[0], q[1]);
        if (n == 2) return f[1];
        
        for (int i = 2; i < n; i++) {
            f[i] = Math.max(f[i - 2] + q[i], f[i - 1]);
        }
        
        return f[n - 1];
    }
}

解法二:状态 DP

分析

状态表示

  • q(i) 表示第 i 家的现金
  • f(i, 0) 表示到第 i 家时,没抢i 家 时的最大现金
  • f(i, 1) 表示到第 i 家时,抢了i 家时的最大现金

状态机

image-20220430162416824

状态转移

  • 没抢i 家,可以从 抢了i - 1 家转移,也可以从 没抢i - 1 家转移
  • 抢了i 家,则只能从 没抢i - 1 家转移

状态计算:最大值 max

代码

class Solution {
    public int rob(int[] q) {
        int n = q.length;
        int[][] f = new int[n][2]; 
        
        f[0][1] = q[0];
        
        for (int i = 1; i < n; i++) {
            f[i][0] = Math.max(f[i - 1][0], f[i - 1][1]);
            f[i][1] = f[i - 1][0] + q[i];
        }
        
        return Math.max(f[n - 1][0], f[n - 1][1]);
    }
}

优化

可见,状态 DP 的状态划分以及转移更加清晰明了,且状态 DP 也更容易优化。

如此题,线性 DP 依赖 i - 2i - 1 状态,而状态 DP 只依赖 i - 1 状态,所以使用滚动变量优化时,状态 DP 更简单

解法一:线性 DP 的滚动变量优化

class Solution {
    public int rob(int[] q) {
        int n = q.length;
        int a = 0, b = q[0], c = Math.max(q[0], q[1]);
        
        for (int i = 2; i < n; i++) {
            int t = Math.max(b + q[i], c);
            a = b;
            b = c;
            c = t;
        }
        
        return c;
    }
}

解法二:状态 DP 的滚动变量优化

class Solution {
    public int rob(int[] q) {
        int n = q.length;
        int a = 0, b = q[0];
        
        for (int i = 1; i < n; i++) {
            int x = a, y = b;
            a = Math.max(x, y);
            b = x + q[i];
        }
        
        return Math.max(a, b);
    }
}

打家劫舍 II

分析

这个和打家劫舍差不多,不同的点是偷了首家就不能偷最后一家,偷了最后一家就不能偷首家。

所以只需要做两次打家劫舍,第一次从第 0n - 2 家,第二次从第 1n - 1 家。

最后在求最大值就是答案。

代码

class Solution {
    public int rob(int[] q) {
        int n = q.length;
        if (n == 1) return q[0];

        int[][] f = new int[n][2];         
        f[0][1] = q[0];        
        for (int i = 1; i < n - 1; i++) {
            f[i][0] = Math.max(f[i - 1][0], f[i - 1][1]);
            f[i][1] = f[i - 1][0] + q[i];
        }
        
        int[][] g = new int[n][2];
        g[1][1] = q[1];
        for (int i = 2; i < n; i++) {
            g[i][0] = Math.max(g[i - 1][0], g[i - 1][1]);
            g[i][1] = g[i - 1][0] + q[i];
        }

        int a = Math.max(f[n - 2][0], f[n - 2][1]);
        int b = Math.max(g[n - 1][0], g[n - 1][1]);
        
        return Math.max(a, b);
    }
}

买卖股票的最佳时机含手续费

分析

状态表示

  • q(i) 表示股票在第 i 天的价格
  • f(i, 0) 表示在第 i 天, 手中 持有 股票时的最大金额
  • f(i, 1) 表示在第 i 天, 手中 没有 股票时的最大金额

状态机

image-20220501013159806

状态转移

  • i 天,没有 股票。即 f(i, 0)
    • 在第 i - 1 天,没有 股票,什么也不做。即 f(i - 1, 0) 转移
    • 在第 i - 1 天,持有 股票,但卖出股票。即 f(i - 1, 1) 转移
  • i 天,持有 股票。即 f(i, 1)
    • 在第 i - 1 天,持有 股票,什么也不做。即 f(i - 1, 1) 转移
    • 在第 i - 1 天,没有 股票,但买入股票。即 f(i - 1, 0) 转移

状态计算:最大值 max,手中不持有股票自然比手中持有股票的金额多

代码

class Solution {
    public int maxProfit(int[] q, int fee) {
        int n = q.length;
        int[][] f = new int[n][2];
        f[0][1] = -q[0];
        for (int i = 1; i < n; i++) {
            f[i][0] = Math.max(f[i - 1][0], f[i - 1][1] + q[i] - fee);
            f[i][1] = Math.max(f[i - 1][1], f[i - 1][0] - q[i]);
        }
        return f[n - 1][0];
    }
}

滚动变量优化后

class Solution {
    public int maxProfit(int[] q, int fee) {
        int n = q.length;
        int x = 0, y = -q[0];
        for (int i = 1; i < n; i++) {
            int a = x, b = y;
            x = Math.max(a, b + q[i] - fee);
            y = Math.max(b, a - q[i]);
        }
        return x;
    }
}

最佳买卖股票时机含冷冻期

分析

状态表示

  • q(i) 表示股票在第 i 天的价格
  • f(i, 0) 表示在第 i 天, 手中 没有股票且在冷冻期 时的最大金额
  • f(i, 1) 表示在第 i 天, 手中 没有股票且非冷冻期 时的最大金额
  • f(i, 2) 表示在第 i 天,手中 持有 股票时的最大金额

状态机

状态转移

  • 在第 i 天,手中 持有 股票时。即 f(i, 2)
    • 在第 i - 1 天,手中 持有 股票。即 f(i - 1, 2) 转移
    • 在第 i - 1 天,手中 没有股票且不非冷冻期,购入股票。即 f(i - 1, 1) 转移
  • i 天,手中 没有股票且在冷冻期 时。即 f(i, 0)
    • 在第 i - 1 天,手中 持有 股票,卖出股票进入冷冻期。即 f(i - 1, 2) 转移
  • i 天,手中 没有股票且非冷冻期 时。即 f(i, 1)
    • 在第 i - 1 天,手中 没有股票且在冷冻期 ,摆脱冷冻期。即 f(i - 1, 0) 转移
    • 在第 i - 1 天,手中 没有股票且非冷冻期,什么都不做。即 f(i - 1, 1) 转移

状态计算:最大值 max,手中不持有股票自然比手中持有股票的金额多(不持有的两种状态都不做限制)

代码

class Solution {
    public int maxProfit(int[] q) {
        int n = q.length;
        int[][] f = new int[n][3];

        f[0][2] = -q[0];

        for (int i = 1; i < n; i++) {
            f[i][2] = Math.max(f[i - 1][2], f[i - 1][1] - q[i]);
            f[i][0] = f[i - 1][2] + q[i];
            f[i][1] = Math.max(f[i - 1][0], f[i - 1][1]);
        }
        
        return Math.max(f[n - 1][0], f[n - 1][1]);
    }
}

滚动变量优化后

class Solution {
    public int maxProfit(int[] q) {
        int n = q.length;
        int a = 0, b = 0, c = -q[0];

        for (int i = 1; i < n; i++) {
            int x = a, y = b, z = c;
            c = Math.max(z, b - q[i]);
            a = z + q[i];
            b = Math.max(x, y);
        }
        
        return Math.max(a, b);
    }
}

买卖股票的最佳时机 IV

买卖股票的最佳时机 III 同此题

分析

状态表示

  • q(i) 表示股票在第 i 天的价格
  • f(i, j, 0) 表示在第 i 天,正进行第 j 次交易,手中 没有 股票时的最大金额
  • f(i, j, 1) 表示在第 i 天,正进行第 j 次交易,手中 持有 股票时的最大金额

状态机

image-20220430162725318

状态转移

  • 在第 i 天,正进行第 j 次交易,手中 没有 股票。即 f(i, j, 0)
    • 在第 i - 1没有 股票,第 i 天也没买入,此时没开始新一轮的交易。从 f(i - 1, j, 0) 转移
    • 在第 i - 1持有 股票,但第 i 天卖出,此时没开始新一轮的交易。从 f(i - 1, j, 1) 转移
  • 在第 i 天,正进行第 j 次交易,手中 持有 股票。即 f(i, j, 1)
    • 在第 i - 1持有 股票,第 i 天没卖出,此时没开始新一轮的交易。从 f(i - 1, j, 1) 转移
    • 在第 i - 1没有 股票,但第 i 天买入,此时开始做新一轮的交易。从 f(i - 1, j - 1, 0) 转移

状态计算:最大值 max,手中不持有股票自然比手中持有股票的金额多

代码

class Solution {
    public int maxProfit(int k, int[] q) {
        int n = q.length;
        if (n == 0 || k == 0) return 0;

        int[][][] f = new int[n][k + 1][2];

        for (int i = 1; i <= k; i++) {
            f[0][i][0] = f[0][i][1] = -0x3f3f3f;
        }

        f[0][0][0] = 0;
        f[0][1][1] = -q[0];

        for (int i = 1; i < n; i++) {
            f[i][0][1] = Math.max(f[i - 1][0][1], f[i - 1][0][0] - q[i]);
            for (int j = 1; j <= k; j++) {
                f[i][j][0] = Math.max(f[i - 1][j][0], f[i - 1][j][1] + q[i]);
                f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - q[i]);
            }
        }

        int res = 0;
        for (int i = 0; i <= k; i++) {
            res = Math.max(res, f[n - 1][i][0]);
        }

        return res;
    }
}

滚动变量优化后

class Solution {
    public int maxProfit(int k, int[] q) {
        int n = q.length;
        if (n == 0 || k == 0) return 0;

        int[][] f = new int[k + 1][2];

        for (int i = 1; i <= k; i++) {
            f[i][0] = f[i][1] = -0x3f3f3f;
        }

        f[0][0] = 0;
        f[1][1] = -q[0];

        for (int i = 1; i < n; i++) {
            int[][] g = new int[k + 1][2];

            for (int j = 0; j <= k; j++) {
                g[j][0] = f[j][0];
                g[j][1] = f[j][1];
            }

            f[0][1] = Math.max(g[0][1], g[0][0] - q[i]);
            for (int j = 1; j <= k; j++) {
                f[j][0] = Math.max(g[j][0], g[j][1] + q[i]);
                f[j][1] = Math.max(g[j][1], g[j - 1][0] - q[i]);
            }
        }

        int res = 0;
        for (int i = 0; i <= k; i++) {
            res = Math.max(res, f[i][0]);
        }

        return res;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值