动态规划

naive的动态规划套路总结

十年热恋 提交于 2020-01-07 01:24:59
\(O(nlogn)\) 求长度为 \(n\) 的数列的 \(LIS\) int LIS(int *a, int n) { int *d = new int[n + 5]; int *g = new int[n + 5]; for(int i=1; i<=n; ++i) g[i] = INF; // INF = 2147483647 for(int i=1; i<=n; ++i) { int k = lower_bound(g+1, g+1+n, a[i]) - g; d[i] = k; g[k] = a[i]; } int ret = 0; for(int i=1; i<=n; ++i) ret = max(ret, d[i]); return ret; } 将 \[d(i,j) = min { d(i+1,j) ~ d(j,j), ... , d(i,j-1) ~ d(i,i) , 0 } \] 中的某些部分保存一下, 可以优化时间复杂度 来源: https://www.cnblogs.com/tztqwq/p/12150354.html

动态规划求RMQ(区间最值问题Range Minimum/Maximum Query)

心已入冬 提交于 2020-01-05 04:14:42
求RMQ最简单的方法是遍历区间,时间复杂度O(n), 但是当查询次数很多的时候,时间效率并不高。可以用线段树把算法优化到O(logn) (在线段树中保存线段的 最值),不过sparse_table算法是较好的,用O(nlogn)的时间进行预处理,然后用O(1)时间进行查询。 预处理: 预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,我们可以开辟一个数组专门来保存f(i, j)的值。 例如,f(0, 0)表示[0,0]之间的最小值,就是num[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值 注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1), j-1)导出, 而递推的初值(所有的f(i, 0) = i)都是已知的 所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。 查询: 假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k, 使得k满足2^k <= (n - m + 1).于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];而我们之前已经求出了f(m, k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1, n]的最小值,

Leetcode 279.完全平方数

烈酒焚心 提交于 2020-01-04 05:31:46
完全平方数 给定正整数 n ,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n 。你需要让组成和的完全平方数的个数最少。 示例 1: 输入: n = 12 输出: 3 解释: 12 = 4 + 4 + 4. 示例 2: 输入: n = 13 输出: 2 解释: 13 = 4 + 9. 【思路】 1. 递归   对于一个数,我们怎么求它的由哪些完全平方数相加得到的呢?   首先找到距离这个数最近的完全平方数m = x*x,我们从1~x中选择一个数,求出n中包含z个x*x,我们在递归求出n-z*x*x所包含的完全平方数。遍历1~x,返回其中最小的结果。 2. 动态规划   动态规划用 dp[i] 数组存储第 i 个数的完美平方数。递推式为:dp[i] = Math.min(dp[j] + dp[i-j], dp[i]),认为 i 的完全平方数是从和为 i 的两个完全平方数 dp[j] 和 dp[i-j]之和,然后从中取最小。 3. 改进后的动态规划      如图所示,红色部分表示平方数,所有的数都可以看做一个普通数加上一个完美平方数,那么递推式就变为了:dp[i + j * j] = Math.min(dp[i] + 1, dp[i + j * j])。 【java递归实现】 1 public class Solution { 2 public

【动态规划】连续子数组的最大和

寵の児 提交于 2020-01-03 11:43:26
题目 输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。 例如,输入数组为[1, -2, 3, 10, -4, 7, 2, -5], 最大和的子数组为[3, 10, -4, 7, 2],因此输出该子数组的和18。 解答 1,动态规划,Time: O(N), Space: O(1) 动态规划, f(i)表示以第i个数字结尾的子数组的最大和, 题目求max(f(i)) f(i) = nums[i] i=0 or f(i-1)≤0 f(i) = f(i-1) + nums[i] i>0 and f(i-1)>0 2,遍历一遍,记录最大和,Time: O(N), Space: O(1) 3,滑动窗口枚举一遍也可以,窗口大小[1, len(n)+1],时间复杂度较高,O(n^2) 这两种方法一样,代码也差不多,第二种方法按自己思路实现的;动态规划更优雅。另外,每种方法都应该合理地处理无效的输入:空、全负数、全0、全正数 代码实现: # class Solution: # # 遍历一遍,记录最大和 # def FindGreatestSumOfSubArray(self, nums): # if not nums: # return 0 # # max = 0 # ans = -1 # 存最大和

动态规划(1):01背包问题

ε祈祈猫儿з 提交于 2020-01-01 19:57:09
题目: 现有n个物品,重量依次为W i(使用int[] weight表示),价值依次为 V i(使用 int[] values表示),现有一个可装重量为17的包(使用bag表示),求使背包物品价值最大化的最优解, 规划方程:f( i , bag )=Max{ ( f( i-1,bag-weight[i])+values[i] ) , f( i-1 ,bag) } 代码示例: /** * 全排列问题(深度搜索字典序) * * @author Swing * */ public class Main { // 物品重量(记得加上一个0) int[] weight = new int[] { 0, 3, 4, 7, 8, 9 }; // 物品的价值 int[] value = new int[] { 0, 4, 5, 10, 11, 13 }; // 包的容量 int bag = 17; // 规划表(初始化都为0) int[][] table = new int[weight.length][bag + 1]; // 最优解(1表示该物品被放入包中,0表示没有) int[] result = new int[weight.length]; int index = result.length - 1; public static void main(String[] args) {

【动态规划】过河卒

混江龙づ霸主 提交于 2020-01-01 18:46:01
原题传送门 思路 思路很简单,到达某个位置的路径的条数一定等于它上方和它右方两格的路径数之和,DP即可。 但实际编码坑点重重,最重要的一点是:数组总TMD越界 !!! 烦......如此水的一道题都用了本蒟蒻40分钟,不敢直视自己的能力QAQ,只能都在墙后瑟瑟发抖...... Code #include<iostream> #include<cstdio> #include<string> #include<vector> #include<algorithm> #include<cstdlib> #include<cmath> #include<stack> #include<map> using namespace std; int a[16][16],dp[16][16]; int main() { int i,j,n,m,x,y; cin>>n>>m>>x>>y; for(i=0;i<=n;i++) for(j=0;j<=m;j++) a[i][j]=1; a[x][y]=0; a[x-1][y-2]=0; a[x-2][y-1]=0; a[x-2][y+1]=0; a[x-1][y+2]=0; a[x+1][y-2]=0; a[x+2][y-1]=0; a[x+2][y+1]=0; a[x+1][y+2]=0; for(i=0;i<=n;i++){if(a[i][0]=

什么是动态规划?

断了今生、忘了曾经 提交于 2020-01-01 00:35:12
什么是动态规划? 斐波那契数列 Fibonacci Sequence F(0) = 1,F(1) = 1,F(n) =F(n-1) + F(n-2) int fib ( int n ) { if ( n == 0 ) { return 0 ; } if ( n == 1 ) { return 1 ; } return fib ( n - 1 ) + fib ( n - 2 ) ; } fib(10) = 55 time : 4 e-06 s fib(20) = 6765 time : 0.000104 s run function fib() 21891 times fib(40) = 102334155 time : 1.28333 s run function fib() 331160281 times fib(42) = 267914296 time : 3.47917 s 指数级别的复杂度,O(2^n) 递归树 : 在递归树中进行了大量的重复计算,run function fib(20) 21891 times;run function fib(40) 331160281 times 记忆化搜索 自上而下的解决问题,利用一个 memo 存放已经计算的结果 vector < int > memo ; int fib ( int n ) { if ( n == 0 ) {

leetCode刷题5—动态规划

匆匆过客 提交于 2019-12-30 19:07:34
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 注意你不能在买入股票前卖出股票。 示例 1: 输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 示例 2: 输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 解法1:暴力解法,因为买了第一天的只能在后边卖,不赚钱又不能卖,所以要赚钱才能卖,而且要赚最多的钱最好。 class Solution: def maxProfit(self, prices: List[int]) -> int: n = len(prices) max_ = 0 for i in range(n): for j in range(i+1,n): if prices[j] > prices[i]: lirun = prices[j] - prices[i] max_ = max(max_,lirun) else: lirun = 0 return max_ 但是时间复杂度太高了。 解法2:动态规划

HDU 1069 动态规划(DP) Monkey and Banana

我只是一个虾纸丫 提交于 2019-12-30 01:35:45
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1069 题意: 有n(n<=30)种 不同的立方体( 每种 个数 不限 ), 求能够 堆多高. 分析: (1) 对于每一种立方体, 假设长,宽 ,高互不相等, 则它 放 置方法有6种不同的情况( 长,宽,高全排列 ) . (2) 那么,实际 上 可以看 成是6*n种不同的立方体 . ( 3 ) 对这6*n种立方体 的 长(如果长相等则以宽) 小到大排序 . (4) 这里就等效于有很多 的箱子排成了一列, 看怎么才能将它堆 得最高, (5) 从小的 一边开始, 如果后面的 箱子上面能放下前面较小的, 就放一个 那种小的 到 那些大箱子上 . (6) 然后将这一堆看成一个箱子,只是这 个箱子比以前加高了,但底面还是不变. (7) 重复 (6) 的操作. 直到最后 没有任何位置的箱子可以放到其位置的箱子上面时, 找到最 高的那个 就 是答案! #include<iostream> #include<string> #include<cstring> #include<algorithm> #include<cstdio> #include<cmath> using namespace std; const int maxn=10000; struct node { int x,y; int