关于dp:经典dp最长上升子序列

1、题目形容 给定一个长度为 N的数列,求数值严格枯燥递增的子序列的长度最长是多少。输出格局第一行蕴含整数 N。第二行蕴含 N 个整数,示意残缺序列。输入格局输入一个整数,示意最大长度。 数据范畴1≤N≤10001−1e9≤数列中的数≤1e9。 输出样例73 1 2 1 8 5 6 输入样例4 2、思路分析 这是一道经典dp问题,咱们要求的是最长回升子序列的长度,这个最长的子序列可能是以a[i]结尾的(咱们假如数字被存储在数组a中),咱们应用一个数组dp[i]来示意以a[i]为结尾的子序列的最大长度,咱们最终所求的就是dp数组中的最大值。 如果咱们需要求dp[i],就是以a[i]为结尾的最大值,那么咱们就须要晓得它的前一个地位是谁,假如它的前一个数字是a[j],那么dp[i] = dp[j] + 1,也就是以它前一个数字结尾的最大子序列长度再+1。j到底是谁咱们不分明,j的范畴是[1, i-1],咱们把最大的(dp[j] + 1)找到,就是最大的dp[i]。 3、代码 #include<iostream>using namespace std;const int N = 1e3+10;int a[N],dp[N];int main(){ int n; cin>>n; int M = 0; for(int i=1;i<=n;++i)cin>>a[i]; for(int i = 1;i<=n;++i)//求dp[i]汇合 { dp[i] = 1;//dp[i]起码也有本身的长度 for(int j=1;j<i;++j)//看看以a[i]结尾的最长子序列的前一个数是哪一个 { if(a[j]<a[i]) { dp[i] = max(dp[i], dp[j]+1); } } //找最长子序列 M = dp[1]; for(int i=2;i<=n;++i) { M = max(M, dp[i]); } } cout<<M<<endl; return 0;}

September 5, 2023 · 1 min · jiezi

关于dp:聊聊动态规划

文章首发于公众号 青梅主码,欢送关注 简介动静布局(Dynamic programming,简称 DP)是美国数学家 Richard Bellman在钻研决策过程和控制系统实践时创立的新办法。它在数学上属于运筹学的一个分支,在数学、管理科学、计算机科学、经济学和生物信息学中均有利用,外围是通过把原问题合成为绝对简略的子问题的形式来求解简单问题,次要利用是求解决策过程最优的数学方法。 简略来讲,动静布局是一种算法思维,其外围是把问题合成为子问题,通过求解子问题进而解决以后问题。理论中,并非所有问题都能够通过动静布局来求解,通过动静布局解决的问题,对问题和其合成对子问题都有肯定场景要求的,动静布局实用于有重叠子问题和最优子结构性质的问题,这类问题应用动静布局所耗时间往往比奢侈解法更少。 如下图,咱们对动静布局问题从利用场景、题解思路和常见示例题目三个方面来开展。 动静布局 Wiki利用场景重叠子问题重叠子问题关注的是一个问题被合成为多个子问题时,子问题和子问题之间的关联。子问题会被反复计算,特地是咱们应用递归自上向下对问题进行求解时,那动静布局是如何解决和优化这些子问题呢?咱们以最简略的斐波那契数列为例来阐明。 斐波那契数列:写一个函数,输出 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:F(0) = 0,F(1) = 1F(N) = F(N - 1) + F(N - 2), 其中 N > 1.示例 1: 输出:n = 2输入:1示例 2: 输出:n = 5输入:5自上而下通过咱们能够通过自上而下的递归办法来解决该问题,代码如下 function fib(n: number): number { // n = 0, f(n) = 0; // n = 1, f(n) = 1; if (n < 2) return n return fib(n - 1) + fib(n - 2)};这样通过递归来解决,代码看起来很简洁,然而仔细分析下,当 n 取值较大时,这其中的反复计算可就太多了,如下,能够简略合成下 ...

January 19, 2022 · 4 min · jiezi

关于dp:leetcode-63DP-不同路径II

思路走到点(i, j),要么是从上边的点走过去的,要么是从右边的点走过去的 达到(i,j)的形式数 = 达到(i-1,j)的形式数 + 达到(i,j-1)的形式数:ways(i, j) = ways(i - 1, j) + ways(i, j - 1)ways(i,j)=ways(i−1,j)+ways(i,j−1)能够用递归,也能够用自下而上的DP:用数组去记录子问题的解(对应递归就是子调用)dpi:达到(i,j)的门路数(形式数)。dpi = dpi - 1 + dpidpi=dpi−1+dpi“阻碍”怎么解决兴许你会想,遇到阻碍我要绕着走,但这种“动静”的想法不合乎 DP “状态”的思路咱们思考单个点的“状态”:阻碍点,是无奈到达的点,是达到形式数为 0 的点,是无奈从它这里走到别的点的点,即无奈提供给别的点形式数的点 base casedp0=1dp0=1 ,出发点就是起点,什么都不必做,形式数 1第一行其余的:以后点走不了,要么是它自身是“阻碍”,要么是它右边的点走不了,否则,门路数是 1,走一条直线过去第一列其余的:以后点走不了,要么是它自身是“阻碍”,要么是它上边的点走不了,否则,门路数是 1,走一条竖线过去 代码工夫复杂度 空间复杂度 都是 O(m*n)。有空会把降维的代码补充上来 const uniquePathsWithObstacles = (obstacleGrid) => { if (obstacleGrid[0][0] == 1) return 0; // 出发点就被阻碍堵住 const m = obstacleGrid.length; const n = obstacleGrid[0].length; // dp数组初始化 const dp = new Array(m); for (let i = 0; i < m; i++) dp[i] = new Array(n); // base case dp[0][0] = 1; // 起点就是出发点 for (let i = 1; i < m; i++) { // 第一列其余的case dp[i][0] = obstacleGrid[i][0] == 1 || dp[i - 1][0] == 0 ? 0 : 1; } for (let i = 1; i < n; i++) { // 第一行其余的case dp[0][i] = obstacleGrid[0][i] == 1 || dp[0][i - 1] == 0 ? 0 : 1; } // 迭代 for (let i = 1; i < m; i++) { for (let j = 1; j < n; j++) { dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1]; } } return dp[m - 1][n - 1]; // 达到(m-1,n-1)的门路数};后记当初写的题解有点啰嗦,慢慢来吧,同时也怕他人看不懂。我记得我刚看源码的时候,搜他人的文章看,本人难了解的中央没有提及,或一笔带过,小白的我只能无奈关掉。心想我本人写肯定要把本人说通,也要他人容易懂,不想让人带着纳闷点进来,又带着纳闷来到。随着我了解的深刻,我会精简表白并欠缺我的题解。 ...

December 19, 2021 · 1 min · jiezi