LeetCode-动态规划

动态规划基本概念

以凑零钱来讲述概念
在这里插入图片描述

1、确定 base case,这个很简单,显然目标金额 amount 为 0 时算法返回 0,因为不需要任何硬币就已经凑出目标金额了。
2、确定「状态」,也就是原问题和子问题中会变化的变量。由于硬币数量无限,硬币的面额也是题目给定的,只有目标金额会不断地向 base case 靠近,所以唯一的「状态」就是目标金额 amount。
3、确定「选择」,也就是导致「状态」产生变化的行为。目标金额为什么变化呢,因为你在选择硬币,你每选择一枚硬币,就相当于减少了目标金额。所以说所有硬币的面值,就是你的「选择」。
4、明确 dp 函数/数组的定义。我们这里讲的是自顶向下的解法,所以会有一个递归的 dp 函数,一般来说函数的参数就是状态转移中会变化的量,也就是上面说到的「状态」;函数的返回值就是题目要求我们计算的量。就本题来说,状态只有一个,即「目标金额」,题目要求我们计算凑出目标金额所需的最少硬币数量。所以我们可以这样定义 dp 函数
dp(n) 的定义:输入一个目标金额 n,返回凑出目标金额 n 的最少硬币数量。

264. 丑数 II

编写一个程序,找出第 n 个丑数。丑数就是质因数只包含 2, 3, 5 的正整数。
在这里插入图片描述
思路
1个丑数也只能是从2,3,5中衍生出来

动态规划: 一个数字可以拆分成若干因子之积,那么我们也可以使用不同因子去构造这个数
定义三个指针 p2、p3、p5 分别乘以 2、3、5 的数字
dp[i] 表示第 i 个丑数。那么 dp[p2] * 2、dp[p3] * 3 和 dp[p5] * 5 中的最小值就是下一个丑数。

小顶堆:
将计算后的全部加入到小顶堆中,注意排重,堆顶就是ans

//动态规划
//一个数的因数也可以构造这个数
int nthUglyNumber(int n) {
    vector<int> dp(n, 0);
    dp[0] = 1;
    //p2表示乘2的指针, p3表示乘3
    int p2 = 0, p3 = 0, p5 = 0;
    for(int i = 1; i < n; i ++){
        //计算当前指针乘相应的数后 取出最小那个,即为构造出的数
        dp[i] = min(min(dp[p2]*2, dp[p3]*3), dp[p5]*5);
        if(dp[i] == dp[p2]*2) p2++;
        if(dp[i] == dp[p3]*3) p3++;
        if(dp[i] == dp[p5]*5) p5++;
    }
    return dp[n - 1];
}

//小顶堆
int nthUglyNumber(int n) {
    priority_queue<long long, vector<long long>, greater<long>> dq;
    set<long long> set;
    dq.push(1);
    set.insert(1);
    vector<int> vt = {2,3,5};
    int ans = 1;
    for (int i = 1; i < n; i++) {
        auto top = dq.top(); dq.pop();
        for (auto t : vt) {
            if (set.find(top * t) == set.end()) {
                dq.push(top * t);
                set.insert(top * t);
            }
        }
        ans = dq.top();
    }
    return ans;
}

313.超级丑数

编写一段程序来查找第 n 个超级丑数。超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。
在这里插入图片描述
思路
丑数问题都可以用小顶堆解决,当然是效率最慢的解决方法

小顶堆
将计算后的值放入队列中,第n次弹出的数就是答案

动态规划
实际就是把之前的3数换成了数组而已, 原理一样,每个primes数都对应indexs数组中记录着指向dp的index

//小顶堆
int nthSuperUglyNumber(int n, vector<int> &primes) {
    priority_queue<long long , vector<long long>, greater<long long>> min_buf;
    unordered_set<long long> set;
    min_buf.push(1);
    set.insert(1);
    long long ans = 1;
    for (int i = 0; i < n; i++) {
       // 当i是1的时候弹出的就是第一个丑数
        ans = min_buf.top(); min_buf.pop();
        for (auto p : primes) {
            long long num = (long long)ans * p;
            //题目不可大于32位
            if (num <=INT32_MAX && set.find(num) == set.end()) {
                min_buf.push(num);
                set.insert(num);
            }
        }
    }
    return ans;
}

//动态规划
int nthSuperUglyNumber(int n, vector<int>& primes) {
        int len = primes.size();
        vector<int> dp(n,1);
        //记录每个指针指向dp的index
        vector<int> index(len,0);
        for(int i = 1; i < n; i++){
            int minNum = INT_MAX;
            //找出最小
            for(int j = 0; j < len; j++) {
                minNum = min(minNum, dp[index[j]]*primes[j]);     
            }
            //找到能够相乘为minNum的指针都+1   
            for(int j = 0; j < len; j++) {
                if(dp[index[j]]*primes[j] == minNum) {
                  index[j]++;
                }  
            }
               
            dp[i] = minNum;
        }
        return dp[n-1];
    }

746.使用最小花费爬楼梯

在这里插入图片描述
思路
要踏上第i级台阶的话, 那么这个台阶最小花费可能来自

  1. 第i - 1 最小花费
  2. 第i - 2 最小花费
    比如你准备踏上第3级台阶
    那么最小花费可能出自 1 , 3 或者 2, 3
public int minCostClimbingStairs(int[] cost) {
		// dp[i] 表示第i个台阶花费最小数
		int[] dp = new int[cost.length];
		int len = dp.length;
		dp[0] = cost[0];
		dp[1] = cost[1];
		for (int i = 2; i < dp.length; i++) {
			dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
		}
		// dp[len - 1] 表示踏到了最后一个台阶
		// dp[len - 2] 表示跳过了最后一个台阶
		return Math.min(dp[len - 1], dp[len - 2]);
	}

354. 俄罗斯套娃信封问题

在这里插入图片描述

思路: 先按w从小到大, 然后按h从小到大
求h的最长上升子序列
public int maxEnvelopes(int[][] envelopes) {
        if (envelopes.length == 0) {
            return 0;
        }
        
        int n = envelopes.length;
        Arrays.sort(envelopes,(e1, e2) ->{
			return e1[0] != e2[0] ? e1[0] - e2[0] : e2[1] - e1[1];
		});

        int[] f = new int[n];
        Arrays.fill(f, 1);
        int ans = 1;
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                // 如果满足可装入条件
                if (envelopes[j][1] < envelopes[i][1]) {
                    // f[i] 表示第i个最大信封数
                    f[i] = Math.max(f[i], f[j] + 1);
                }
            }
            ans = Math.max(ans, f[i]);
        }
        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值