动态规划

[动态规划]石子合并问题

半世苍凉 提交于 2019-12-06 01:45:23
https://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/3016/pid/1729 石子合并问题 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。 对于给定n堆石子,计算合并成一堆的最小得分和最大得分。 Input 输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。 Output 输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。 Sample Input 4 4 4 5 9 Sample Output 43 54 算法考试考完了(题目有点简单侥幸AK..),但这个问题当时困扰我了很久,网上解答的很模糊,因此还是在这里记录一下吧。这题的难点是:如果将环形转换为直线(直线的网上有很多解答,这里不做赘述)。其核心思想就是:通过将数量变为 2n-1 来转换成直线问题。当时看到这几行字困惑了很久,一直不太理解,但之后画图就懂了。 1.

贪心算法(会场安排问题、区间选点)

淺唱寂寞╮ 提交于 2019-12-05 09:18:20
学习算法课程之后的第一次记录,渐渐的,程序设计考虑的因素增多,程序=数据结构+算法,这个等式让我深有体会。从开始简单的C++编程,再到选择合适数据结构,现在需要更进一步,从算法层次上考虑程序执行的效率。我对算法的理解是用更少的开销获得更优的执行效果。 分治法、动态规划在此之前没有记录下来,学到贪心算法的时候,觉得需要总结一下学过的东西,也能更好的理解。动态规划的设计,要满足最优子结构性质和重叠子问题,采用自底向上的策略,计算出最优值,找到整体最优解。这个过程有时候挺难的,主要在写出递归式,要自底向上填表。贪心策略有点像动态规划,但在一些方面是不同的,有时候贪心算法的思想更容易想到。它要满足子问题最优而得到整体最优?两个条件:最优子结构性质和贪心选择性质。满足贪心选择性质一定满足最优子结构性质,而满足最优子结构性质不一定满足贪心选择性质, 比如背包问题可以用贪心算法解决,而0-1背包问题只能用动态规划。 典型的贪心问题活动安排,有n个活动,给出开始时间和结束时间,要尽可能安排多的活动(时间互相不冲突)。解决这个问题正确的贪心思想是以每个活动结束时间为比较变量,按结束时间升序排好活动次序,接着就进行比较选择。而会场安排问题与活动又有些不同之处,下面是我的解题过程。 7-2 会场安排问题 (20 分) 假设要在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的

鸡蛋掉落--动态规划

假装没事ソ 提交于 2019-12-05 07:24:13
你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。 每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。 你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。 每次 移动 ,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N )。 你的目标是确切地知道 F 的值是多少。 无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少? 示例 1: 输入:K = 1, N = 2 输出:2 解释: 鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。 否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。 如果它没碎,那么我们肯定知道 F = 2 。 因此,在最坏的情况下我们需要移动 2 次以确定 F 是多少。 示例 2: 输入:K = 2, N = 6 输出:3 思想: 解法: class Solution { public int superEggDrop(int K, int N) { return dp(K, N); } Map<Integer, Integer> memo = new HashMap(); public int dp(int K, int N) { if (!memo

《动态规划》递推,递归,HelpJimmy

我的梦境 提交于 2019-12-05 06:06:45
https://www.jianshu.com/p/7799f6ede3f2 /* 记忆递归的程序 */ #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define MAX_N 1000 #define INFINITE 1000000 int t, n, x, y, maxHeight; struct Platform { int Lx, Rx, h; bool operator<(const Platform &p2) const { return h > p2.h; } }; Platform platForms[MAX_N + 10]; int leftMinTime[MAX_N + 10]; int rightMinTime[MAX_N + 10]; int L[MAX_N + 10]; int MinTime(int I, bool bLeft) { int y = platForms[I].h; int x; if (bLeft) x = platForms[I].Lx; else x = platForms[I].Rx; int i; for (i = I + 1; i <= n; i++) { // make sure

最小路径和 -- 动态规划

落爺英雄遲暮 提交于 2019-12-04 20:48:02
给定一个包含非负整数的m * n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总合为最小. 说明: 每次只能向下或者向右移动一下. 示例: 输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和最小。 解法一: 动态规划 思想 因为最近在做动态规划的专题,所以用动态规划的思想来解决本题目: 我们新建一个dp数组,用来保存每走一步的最短路径,但是最后一个数值也就是最后一个元素肯定是有的; 我们利用递推的公式: dp(i,j)=grid(i,j)+min(dp(i+1,j),dp(i,j+1)) 即可,思想比较特殊,但是找到规律后还是很好理解的. 代码 public int minPathSum(int[][] grid) { int[][] dp = new int[grid.length][grid[0].length]; for (int i = grid.length - 1; i >= 0; i--) { for (int j = grid[0].length - 1; j >= 0; j--) { if(i == grid.length - 1 && j != grid[0].length - 1)   dp[i][j] = grid[i][j] + dp[i][j + 1]; else if

动态规划专题

半城伤御伤魂 提交于 2019-12-04 17:41:59
一、记忆化搜索 斐波那契算法 1.递归代码(时间复杂度O(2^n)): int f(int n){ if(n == 1 || n == 2){ return 1; } return f(n-1) + f(n-2); } 2.递归加记忆化 public class Solution{ int[] res = new int[n]; Arrays.fill(res, -1); int f(int n){ if(n == 1 || n == 2){ return 1; } if(res[n] == -1) return f(n-1) + f(n-2); return res[n]; } } 3.动态规划 或者 转化为非递归 或者转化为矩阵求解 递归是自上而下的解决问题(如求f(n)就已经假设所有小于n的值已经得到,) 动态规划是自下而上的解决问题(求f(n) 就已经把前面n-1个值完全获得) 动态规划 约等于 记忆化搜索 (严格来说不对 因而 记忆化搜索也是自上而下,动态规划是自下而上的解决问题 ) 70. Climbing Stairs You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many

0x50 动态规划

a 夏天 提交于 2019-12-04 13:19:45
0x51 线性DP LIS 最长上升子序列,给定一个长度为 \(N​\) 序列 \(A​\) ,求出数值单调递增的子序列长度最长是多少 \(O(n^2)\) 做法 \(f[i]\) 表示以 \(i\) 结尾的最长上升子序列长度是多少 自然转移方程为 \(f[i]=max(f[j])+1,(1\le j < i,A[j]<A[i] )\) for( register int i = 1 ; i <= n ; i ++ ) { f[i] = 1; for( register int j = 1 ; j < i ; j ++) { if( a[j] >= a[i] ) continue; f[i] = max( f[i] , f[j] + 1 ); } } \(O(nlog_n)\) 做法 对于 \(O(n^2)\) 的做法,我们每次都枚举 假设我们已经求的一个最长上升子序列,我们要进行转移,如果对于每一位,在不改变性质的情况下,每一位越小,后面的位接上去的可能就越大,所以对于每一位如果大于末尾一位,就把他接在末尾,否则在不改变性质的情况下,把他插入的序列中 for( register int i = 1 ; i <= n ; i ++ ) { if( a[i] > f[ tot ] ) f[ ++ tot ] = a[i]; else *upper_bound( f + 1 , f +

动态规划四步解题法模板

六眼飞鱼酱① 提交于 2019-12-04 09:43:21
导言 动态规划问题一直是算法面试当中的重点和难点,并且动态规划这种通过空间换取时间的算法思想在实际的工作中也会被频繁用到,这篇文章的目的主要是解释清楚 什么是动态规划 ,还有就是面对一道动态规划问题,一般的 思考步骤 以及其中的注意事项等等,最后通过几道题目将理论和实践结合。 什么是动态规划 如果你还没有听说过动态规划,或者仅仅只有耳闻,或许你可以看看 Quora 上面的这个 回答 。 用一句话解释动态规划就是 “ 记住你之前做过的事 ”,如果更准确些,其实是 “ 记住你之前得到的答案 ”。 我举个大家工作中经常遇到的例子。 在软件开发中,大家经常会遇到一些系统配置的问题,配置不对,系统就会报错,这个时候一般都会去 Google 或者是查阅相关的文档,花了一定的时间将配置修改好。 过了一段时间,去到另一个系统,遇到类似的问题,这个时候已经记不清之前修改过的配置文件长什么样,这个时候有两种方案,一种方案还是去 Google 或者查阅文档,另一种方案是借鉴之前修改过的配置,第一种做法其实是万金油,因为你遇到的任何问题其实都可以去 Google,去查阅相关文件找答案,但是这会花费一定的时间,相比之下,第二种方案肯定会更加地节约时间,但是这个方案是有条件的,条件如下: 之前的问题和当前的问题有着关联性,换句话说,之前问题得到的答案可以帮助解决当前问题 需要记录之前问题的答案

判断子序列--动态规划

核能气质少年 提交于 2019-12-04 09:14:27
给定字符串 s 和 t ,判断 s 是否为 t 的子序列. 你可以认为 s 和 t 中仅包含英文小写字母. 字符串 t 可能会很长(长度约等于500000), 而 s 是个短字符(长度<= 100). 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。 示例1: s = "abc", t = "ahbgdc" 返回 true. 示例2: s = "axc", t = "ahbgdc" 返回 false 解法一: 动态规划 算法思想 当char[i]==char[j]时,则字符i一定是j的子序列,如果0~i-1子字符串是0~j-1子字符串的子序列,则dp[i][j]=true,所以dp[i][j] = dp[i-1][j-1]; 当char[i]!=char[i]时,即判断当前0~i子字符串是否是0~j-1的子字符串的子序列,即dp[i][j] = dp[i][j - 1]。如ab,eabc,虽然s的最后一个字符和t中最后一个字符不相等,但是因为ab是eab的子序列,所以ab也是eabc的子序列 初始化:空字符串一定是t的子字符串的子序列,所以dp[0][j]=true swift代码 func isSubsequence(_ s: String, _ t:

程序员必须掌握的核心算法有哪些?

落花浮王杯 提交于 2019-12-04 08:14:55
由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我, 数据结构与算法应该要学习到哪个程度呢? ,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,我稍微总结一下我学过的算法知识点,以及我觉得值得学习的算法。这些算法与数据结构的学习大多数是 零散 的,并没有一本把他们全部覆盖的书籍。下面是我觉得值得学习的一些算法以及数据结构,当然,我也会整理一些看过不错的文章给大家。大家也可以留言区补充。 一、算法最最基础 1、时间复杂度 2、空间复杂度 一般最先接触的就是时间复杂度和空间复杂度的学习了,这两个概念以及如何计算,是必须学的,也是必须最先学的,主要有最大复杂度、平均复杂度等,直接通过博客搜索学习即可。 文章推荐: 算法分析神器—时间复杂度 二、基础数据结构 1、线性表 列表(必学) 链表(必学) 跳跃表(知道原理,应用,最后自己实现一遍) 并查集(建议结合刷题学习) 不用说,链表、列表必须,不过重点是链表。 三分钟基础数据结构:如何轻松手写链表? 以后有面试官问你「跳跃表」,你就把这篇文章扔给他 2、栈与队列 栈(必学) 队列(必学) 优先队列、堆(必学) 多级反馈队列(原理与应用) 特别是优先队列,再刷题的时候,还是经常用到的,队列与栈,是最基本的数据结构,必学。可以通过博客来学习。相关文章: 三分钟基础知识:什么是栈?