动态规划

动态规划

标签(空格分隔): algorithm
作业部落地址:https://www.zybuluo.com/LIUHUAN/note/1465029


最长连续递增子序列

  • 最长连续递增子序列
  • 题目大意:在一个数字序列中找到最长的且连续的递增子序列的长度。
  • 例如:序列:[1,3,5,4,7] 最长连续子序列长度为3,即[1,3,5]
  • 又如: 序列:[2,2,2,2,2] 最长连续子序列长度为1,即[2]

1、思路一

  • 计算每个元素开始或者结束的连续子序列,记录最大的值
int findLengthOfLCIS(vector<int>& nums) {
        int n = nums.size();
        if(n <= 1)
            return n;
        int maxlen = 1;
        for(int i = 0; i < n;i++ )  {
            int len = 1;// 每次nums[i] 开始的序列最大长度为1,往后增长
            for(int j = i+ 1;j < n;j++){
            if(nums[j] > nums[j-1] ) {// 连续递增
                len += 1; // 长度增加
                maxlen = max(maxlen,len);
            }else {// 不连续递增了,下一个元素继续计算
                break;
            }
        }
        return maxlen;
    }

2、思路2

  • 假设我们知道了某个元素结尾的最长连续子序列的长度,那么下一个元素,要么比其前一个元素大,一起构成更长的一个连续子序列,要么自己独自成一个子序列长度为1。
  • 假设我们有dp[i] 表示以nums[i]结尾元素的最长连续子序列长度
  • 那么下一个元素nums[i+1] 与nums[i] ,要么nums[i+1] > nums[i] 这时可以构成一个更长的连续递增子序列且以nums[i+1]结尾,长度为dp[i] + 1。要么nums[i+1] <= nums[i],这时以nums[i+1]结尾的最长连续子序列长度为1.
  • 初始状态是:dp[0] = 1
  • 综合上述两种情况,可以得到如下的表达式

dp[i+1] = \left\{\begin{matrix} dp[i] +1 & if & nums[i+1] > nums[i] \\ 1 & else \end{matrix}\right.

int findLengthOfLCIS(vector<int>& nums) {
        int n = nums.size();
        if(n <= 1)
            return n;
        vector<int> dp(n,1);
        int maxlen = 1;
        for(int i = 1; i < n; i++ )  {
            if( nums[i] > nums[i-1] ) {
                dp[i] = dp[i-1] + 1 ;
            }
            maxlen = max( dp[i] , maxlen );
        }
        return maxlen;
    }

  • 时间复杂度:O(n),空间复杂度:O(n)
  • 由于只需要获取最长的长度,所以这里可以优化空间复杂度

最长递增子序列

  • 最长递增子序列
  • 题目的意思是:在一个数字序列中找到最长的且是递增的子序列,返回这个最长子序列的长度
  • 与上一个不同的是,这里的序列不需要是连续的
  • 例如:[10,9,2,5,3,7,101,18] 序列的最长递增子序列为[2,3,7,101]

1、思路1

  • 由上一题的思路可以知道,如果知道nums[i] 结尾的最长递增子序列为dp[i] 那么,以nums[i+1] 结尾的最长子序列,肯定是i+1前面的某个子序列(可能为空)以num[k]结尾,加上nums[i+1]构成的。也可以从另一个角度理解,如果i+1前面的某个数nums[k] 小于nums[i+1] 那么,{nums[k],nums[i+1]}可以构成一个比原来更长的子序列。
  • 初始状态:dp[0] = 1
  • 综合上述说明:可以得到如下的递推公式
    dp[i] = \underset{0 \leqslant k< i }{max}\left\{\begin{matrix} dp[k] + 1 & if & nums[i] > nums[k] \end{matrix}\right.
int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        if(n <= 1 )
            return n;
        vector<int> dp(n,1);// 初始化为1
        int max_len = 1;
        for(int i = 1 ;i < n; i++) {
            for(int j = i-1 ;j >=0 ;j-- ) { // 遍历[0,i-1]的元素加上nums[i] 之后的长度是否构成更长的递增子序列
                if(nums[j] < nums[i]) {
                    dp[i] = max(dp[i],dp[j] + 1 );
                    max_len = max(max_len,dp[i]);
                }
            }
        }
        return max_len;
    }
  • 时间复杂度:O(n^2),空间复杂度:O(n)

最长递增子序列的数量

  • 最长递增子序列的个数
  • 题目大意:在第二题的基础上,找到所有长度等于最大长度的递增子序列的长度的个数
  • 例如:[1,3,5,4,7],最长递增子序列的个数为2, 即,[1, 3, 4, 7] 和 [1, 3, 5, 7]。又如:[2,2,2,2,2],最长递增子序列的个数为5,所有的长度为1的序列都是最长递增子序列

1、思路1

  • 上一题中记录了最长递增子序列的长度。并没有记录这个长度的子序列的个数
  • 所以我们通过计算每一个元素为结尾的最长递增子序列的来源个数,来获得最大长度递增子序列的个数
  • 具体的来说,假设f[i]表示以nums[i]结尾的元素的最长子序列个数,dp[i]表示以nums[i]结尾的最长子序列的长度,那么找到最长的子序列长度dp[k],就可以找到其对应的长度来源的个数f[k]。那么f[k]如何计算呢?
  • f[k] 依赖于dp[k],如果dp[k]是从dp[m]扩展而来,那么f[k] = f[m],如果dp[k] 还可以从dp[x]扩展而来,那么f[k] = f[m] + f[x]...可以看出来,f[k]等于能够从之前的某个子序列扩展到dp[k]而来的所有序列个数之和。
  • 得到如下的公式:
  • 初始情况:dp[i] =1,f[i]=1;
    dp[i] = \underset{0 \leqslant k< i }{max}\left\{\begin{matrix} dp[k] + 1 & if & nums[i] > nums[k] \end{matrix}\right. \\ f[i] = \sum_{ nums[i] > nums[k] \&\& dp[k] + 1 == dp[i] } f[k]
  • 最后需要依据最大长度的递增子序列求和所有的来源个数。
int findNumberOfLIS(vector<int>& nums) {
        int n = nums.size();
        if(n <= 1)
            return n;
        vector<int> dp(n,1); // nums[i]结尾的最长递增子序列长度
        int max_len = 1;//最大长度
        vector<int> max_c(n,1);// 每个最长子序列的长度默认为1
        for(int i = 1; i < n; i++ ){                                 
            for(int j = i - 1; j >= 0; j-- ) {
                if(nums[i] > nums[j]) {
                    if(dp[i] < dp[j] + 1){// dp[i] 可以从dp[j] 扩展得到更长的子序列,那么最长子序列的个数就是扩展前dp[j]的个数,max_c[j]
                        max_c[i] = max_c[j]; // 扩展后的来源个数
                    }else if(dp[i] == dp[j] + 1) { // 相同长度的其他扩展而来的子序列 
                        max_c[i] += max_c[j] ; // 因为长度相同,所以求和来源的个数
                    }
                    dp[i] = max(dp[i],dp[j] + 1);
                    max_len = max(dp[i],max_len);
                }
            }
        }
        // 遍历所有的dp[i]等于最大长度时,求和所有的max_c[i],得到最大长度递增子序列的个数和
        int maxc = 0;
        for(int i = 0; i < n ; i++) {
            if(dp[i] == max_len) {
                maxc += max_c[i];
            }
        }
        return maxc;
    }
  • 时间复杂度:O(n^2);空间复杂度:O(n)

最长配对链

  • 最长配对链
  • 题目大意:给定元素为两个数的元组的数组,元组的属性(a,b) a < b,如果两个元素x=(a,b),y=(c,d) 满足b<c 那么(x,y)构成一个链,长度为2,这个题求的是所有元素中能够成这个链的最大长度。其中元素序列可以任意选取。

思路1

  • 本题与最长递增子序列非常相似,不同点在于,这里可以随意选取元素,构成组合,如果按照元组的第一个元素排序,就能够得到和最长递增子序列的问题一致的情况。
  • 初始情况:dp[i] = 1;
int findLongestChain(vector<vector<int>>& pairs) {
        int n = pairs.size();
        if(n <= 1)
            return n;
        sort(pairs.begin(),pairs.end(),[](vector<int>& a,vector<int>&b ){return a[0] < b[0];});
        vector<int> dp(n,1);
        int max_len = 1;
        for(int i =1;i < n; i++) {
            int ifirst = pairs[i][0];
            for(int j =i -1 ;j >= 0; j--) {
                if(pairs[j][1] < ifirst) { // 构成递增链
                    dp[i] = max(dp[i],dp[j] + 1); //更新递增链大小
                    max_len = max(max_len,dp[i]);
                }
            }
        }
        return max_len;
    }
  • 时间复杂度:O(n^2) 空间复杂度:O(n)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容