【算法集训 | 暑期刷题营】8.1题---线性dp

简介: 【算法集训 | 暑期刷题营】8.1题---线性dp

👉引言


铭记于心
🎉✨🎉我唯一知道的,便是我一无所知🎉✨🎉


💖 ❄️我们的算法之路❄️💖


   众所周知,作为一名合格的程序员,算法 能力 是不可获缺的,并且在算法学习的过程中我们总是能感受到算法的✨魅力✨。

☀️🌟短短几行代码,凝聚无数前人智慧;一个普通循环,即是解题之眼🌟☀️

💝二分,💝贪心,💝并查集,💝二叉树,💝图论,💝深度优先搜索(dfs) ,💝宽度优先搜索(bfs) ,💝数论,💝动态规划等等, 路漫漫其修远兮,吾将上下而求索! 希望在此集训中与大家共同进步,有所收获!!!🎉🎉🎉

image.png


今日主题:线性dp


【概述】


线性动态规划,是较常见的一类动态规划问题,其是在线性结构上进行状态转移,这类问题不像背包问题、区间DP等有固定的模板
线性动态规划的目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值

因此,除了少量问题(如:LIS、LCS、LCIS等)有固定的模板外,大部分都要根据实际问题来推导得出答案

👉⭐️第一题💎


   ✨题目


      剑指 Offer 42. 连续子数组的最大和

image.png


   ✨思路:


归,遍历所有的子数组,寻找和最大的一组,需要注意的是在该段代码中,递归出口如果设置为=0时return nums[index] 则在数组只有一个元素时需要去手动更新ans


   ✨代码:


class Solution {
public:
int process(vector<int> &nums, int index, int &ans)
{
    if (index < 0)
        return 0;
    int tem = max(nums[index], nums[index] + process(nums, index - 1, ans));
    ans = max(ans,tem);
    return tem;
}
int maxSubArray(vector<int> &nums)
{
    int ans = INT_MIN;
    process(nums, nums.size() - 1,ans);
    return ans;
}
};

image.png


 👉⭐️第二题💎


   ✨题目


      300. 最长递增子序列

image.png

由暴力递归改成记忆化搜索,再通过打表优化成动态规划,这是一个二维dp。主要思想就是根据最长公共子序列模板,先将nums数组处理成严格递增的一个新数组nums1,然后寻找nums与nums1的最长公共子序列,得到的就是nums的严格递增序列


   ✨代码:


class Solution {
public:
int lengthOfLIS(vector<int> &nums)
{
    set<int>tem(nums.begin(), nums.end());
    vector<int> nums1(tem.begin(), tem.end());
    vector<vector<int>> dp(nums.size() + 1, vector<int>(nums1.size() + 1));
    for (int i = 1; i <= nums.size(); i++)
    {
        for (int j = 1; j <= nums1.size(); j++)
        {
            if (nums[i - 1] == nums1[j - 1])
            {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            else
            {
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
            }
        }
    }
    return dp[nums.size()][nums1.size()];
};
};

image.png


 👉⭐️第三题💎


   ✨题目


      2320. 统计放置房子的方式数

image.png


   ✨代码:


class Solution {
public:
int dp(int index, int n,vector<int>&DP)
{int tar = 1e9 + 7;
    if(DP[index])return DP[index];
    if (index >= n)
    {
         DP[index] =1;
         return DP[index];
    }
   DP[index]=(dp(index + 2, n,DP)%tar + dp(index + 1, n,DP)%tar)%tar;
   return DP[index];
}
int countHousePlacements(int n)
{
    int tar = 1e9 + 7;
    vector<int> DP(n+3);
    long long ans = dp(0, n,DP);
    long long A = (ans % tar) * (ans % tar)%tar;
    return A;
}
};


 👉⭐️第四题💎


   ✨题目


      4.Computer Game

image.png


   ✨思路:


看到这题我首先想到DFS,即将所有能走的路线都尝试一遍,如果最终能到达终点,则打印YES,然而思路虽然对了,但是对于这题来讲明显会超时,即使改记忆化搜索也没有用

image.png

但是很明显这道题并不需要dfs,只需按列遍历数组,如果同一列都是1,那么则无论如何都没有路,就直接打印NO即可,下面是先用DFS的暴力搜索代码,运行一般的测试用例没有错误,但是放到Oj上会TLE


   ✨代码:


#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> dp;
int pro(vector<string> &nums, int x, int y)
{
    if (dp[x][y])
        return dp[x][y];
    if (nums[x][y] == '1')
    {
        dp[x][y] = 0;
        return dp[x][y];
    }
    if (x == 1 && y == nums[0].size() - 1)
    {
        dp[x][y] = 1;
        return dp[x][y];
    }
    int doo[][2] = {{0, 1}, {1, 0}, {1, 1}, {-1, 1}};
    int X = x, Y = y;
    for (int i = 0; i < 4; i++)
    {
        x = x + doo[i][0];
        y = y + doo[i][1];
        if (x < 0 || x > 1 || y > nums[0].size() - 1)
        {
            x = X;
            y = Y;
            continue;
        }
        int f = pro(nums, x, y);
        if (f)
        {
            dp[x][y] = 1;
            return dp[x][y];
        }
    }
    return dp[x][y];
}
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        int m;
        cin >> m;
        vector<string> nums(2);
        dp.resize(2, vector<int>(m));
        for (int t = 0; t < 2; t++)
        {
            cin >> nums[t];
        }
        int f = pro(nums, 0, 0);
        if (f)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
        dp.clear();
    }
    system("pause");
    return 0;
}


  • 更改后的正确代码

#include <bits/stdc++.h>
using namespace std;
int pro(vector<string> &nums)
{
    for(int i=0; i<nums[0].size(); i++) {
        if(nums[0][i] == '1' && nums[1][i] == '1')
            return 0;
    }
    return 1;
}
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        int m;
        cin >> m;
        vector<string> nums(2);
        for (int t = 0; t < 2; t++)
        {
            cin >> nums[t];
        }
        int f = pro(nums);
        if (f)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
  system("pause");
    return 0;
}


  ⭐️总结⭐️


动态规划其实就是暴力递归,最终优化成打表的形式,初学者可能不能立即推出状态转移方程,所以不妨先试试递归思想,试错的成本也很低

写在最后

相信大家对今天的集训内容的理解与以往已经有很大不同了吧,或许也感受到了算法的魅力,当然这是一定的,路漫漫其修远兮,吾将上下而求索!伙伴们,明天见!


相关文章
|
3天前
|
算法 Java 机器人
Java数据结构与算法:查找算法之线性查找
Java数据结构与算法:查找算法之线性查找
|
5天前
|
存储 算法 C语言
【数据结构与算法 刷题系列】合并两个有序链表
【数据结构与算法 刷题系列】合并两个有序链表
|
16天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
16天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
5天前
|
算法
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
|
5天前
|
算法
【数据结构与算法 刷题系列】判断链表是否有环(图文详解)
【数据结构与算法 刷题系列】判断链表是否有环(图文详解)
|
5天前
|
算法
【数据结构与算法 刷题系列】移除链表元素
【数据结构与算法 刷题系列】移除链表元素
|
5天前
|
存储 算法 C语言
【数据结构与算法 刷题系列】环形链表的约瑟夫问题
【数据结构与算法 刷题系列】环形链表的约瑟夫问题
|
5天前
|
算法 C语言
【数据结构与算法 刷题系列】求链表的中间结点
【数据结构与算法 刷题系列】求链表的中间结点
|
6天前
|
存储 算法 Java
Java数据结构与算法:线性数据结构之数组
Java数据结构与算法:线性数据结构之数组