ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

算法笔记之初识DP(一)

2020-08-22 05:00:28  阅读:301  来源: 互联网

标签:vector return int res memo coins 算法 初识 DP


动态规划(DP)不是某种具体算法,而是一种思想。把大问题转化为小问题,利用小问题的解推断出大问题就是DP的核心思想。

  • step 1: 设计状态。把面临的每一个问题,用状态表达出来。
  • step 2: 设计转移。写出状态转移方程,从而利用小问题的解推出大问题的解。

两种状态转移的方法

  • 1.pull型:从哪来, 从0到n
  • 2.push型:到哪去, 从n到0

e.g.上楼梯问题
pull: f[n] = f[n-1] + f[n-2];
push: f[n] -> f[n+1] += f[n]
f[n+2] += f[n]

push能用到f[0]的信息,可能会好下手

1.暴力枚举 与 记忆化

暴力枚举保存了“每一步采取什么操作”的信息,其为指数级复杂度, 且对重复进行递归,浪费性能

70.Climbing Stairs

  • 想知道的信息
    从 0 级走到 n 级的方案数 f(n).
  • 为了得到f(n),我们需要什么信息
    知道 f(n) 和 f(n − 1) 即可。

只需要f[1] 和 f[2]

暴力模拟,很多重复的步骤, 比如climbStairs(n)出现很多次,没意义。

// TLE
class Solution {
public:
    int climbStairs(int n) {
        if (n == 1) return 1;
        else if (n == 2) return 2;
        return climbStairs(n-1) + climbStairs(n-2);
    }
};

需要一种更好的算法,比如保留信息f[x]来表示从0级走到x级的方案数,假设f[1], f[2], .., f[n-1]已知,很容易退出下一步。
pull

class Solution {
public:
    int memo[46] = {0};
    int climbStairs(int n) {
        if (n == 1) return 1;
        else if (n == 2) return 2;
        memo[1] = 1; memo[2] = 2;
        for (int i = 3; i<=n; i++) {
            memo[i] = memo[i-1] + memo[i-2];
        }
        return memo[n];
    }
};

push

class Solution {
public:
    int memo[47] = {0};
    int climbStairs(int n) {
        memo[0] = 1;
        for (int i=0; i<n; i++) {
            memo[i+1] += memo[i];
            memo[i+2] += memo[i];
        }
        return memo[n];
    }
};

322. Coin Change

  • 想知道的信息
    凑到n块钱所需的最少硬币数f(n).
  • 为了得到f(n),我们需要什么信息
    知道 f(n-1) 和 f(n−5) 即可。(当硬币类型位1,5时)

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {    
        vector<int> memo(amount+1, 0x7ffffff0);
        // push
        memo[0] = 0;
        for (int i=0; i<=amount; i++ ) {
            for (int j=0; j<coins.size(); j++) {
                if (coins[j]  <= amount - i) {
                    memo[i+coins[j]] = min(memo[i+coins[j]], memo[i]+1);
                }
            }
        }
        return memo[amount] != 0x7ffffff0? memo[amount] : -1;
    }
};

优化版->按币的值来跳 + greedy

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int res = INT_MAX, n = coins.size();
        sort(coins.begin(), coins.end());
        helper(coins, n - 1, amount, 0, res);
        return (res == INT_MAX) ? -1 : res;
    }
    void helper(vector<int>& coins, int start, int target, int cur, int& res) {
        if (start < 0) return;
        if (target % coins[start] == 0) {
            res = min(res, cur + target / coins[start]);
            return;
        }
        for (int i = target / coins[start]; i >= 0; --i) {
            if (cur + i >= res - 1) break;
            helper(coins, start - 1, target - i * coins[start], cur + i, res);
        }
    }
};

狗屁不通生成器

300. Longest Increasing Subsequence

f[x] 表达的是“以 a[x] 结尾的最长的上升子序列长度”。


push

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if (nums.empty()) return 0;
        vector<int> memo(nums.size(), 1);

        for (int i=1; i<nums.size(); i++) {
            for (int j=0; j<i; j++) {
                if (nums[i] > nums[j] && memo[i] < memo[j]+1) {
                    memo[i] = memo[j]+1;
                }
            }
        }
        return *max_element(memo.begin(), memo.end());
    }
};

二分

iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。
iterator upper_bound( const key_type &key ):返回一个迭代器,指向键值> key的第一个元素。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int>vec;
        for(auto n:nums){
            // lower_bound 用来找是哪个pile, 不存在比n大的情况,lower_bound返回vec.end(), 加入新的pile,所以有多少pile就有多大
            auto it = lower_bound(vec.begin(),vec.end(),n);
            if(it!=vec.end()){
                *it=n;
            }else{
                vec.push_back(n);
            }
        }
        return vec.size();
    }
};

https://www.youtube.com/watch?v=22s1xxRvy28&feature=youtu.be

1143. Longest Common Subsequence

记 f(x, y) 表示 A 的前 x 个元素,与 B 的前 y 个元素的 LCS

  • f(n, m) 可以是 f(n − 1, m) -> a[n] != b[m]
  • f(n, m) 可以是 f(n, m − 1) -> a[n] != b[m]
    如果 a[n] = b[m],则 f(n, m) 可以取 f(n − 1, m − 1) + 1

push

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        vector<vector<int>> memo (text1.size()+1, vector<int> (text2.size()+1));
        
        // f(x,y)
        // f(x+1,y)
        for (int i=0; i<text1.size(); i++) {
            for (int j=0; j<text2.size(); j++) {
                if (text1[i] == text2[j]) memo[i+1][j+1] = memo[i][j]+1;
                else {
                    memo[i+1][j+1] = max(memo[i][j+1], memo[i+1][j]);
                }
            }
        }
        return memo[text1.size()][text2.size()];
    }
};

P1464.Function

记忆化搜索模板题

标签:vector,return,int,res,memo,coins,算法,初识,DP
来源: https://www.cnblogs.com/linsinan1995/p/13543229.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有