动态规划

经典数塔问题(动态规划)

妖精的绣舞 提交于 2020-01-20 16:18:38
有形如下图所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的值最大。 从顶点出发时到底是向左走还是向右走应取决于向左走能取得最大值还是向右走能取得最大值,只有两条路径上的最大值求出来了才能做出决策, 即dp[1][1] = max(dp[2][1],dp[2][2]) 故由此推得状态转移方程为dp[i] = max(dp[i+1][j],dp[i+1][j+1]); 边界条件即最下边一层的数据 //数塔问题 #include<iostream> using namespace std; int main() { int n; int f[100][100] = {0}; int dp[100][100] = {0};//状态数组 while(cin>>n) { memset(f,0,sizeof(f)); memset(dp,0,sizeof(dp)); //输入数塔 for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>f[i][j]; //边界,最底下一层的数塔dp值等于f值 for(int j=1;j<=n;j++) dp[n][j] = f[n][j]; for(int i=n-1;i>=1;i--) { for(int j=1;j<=i;j++) { //状态转移方程 dp[i

简单理解算法篇--动态规划

霸气de小男生 提交于 2020-01-20 10:20:27
动态规划方法通常用来求解最优化问题,这些问题有很多种解,但我们希望寻求最优解。 满足两个条件既可以使用动态规划 1.具有最优子结构 2.子问题重叠 至于这两点是什么意思?先看个问题 现在有个钢筋长度和价格对应的表,问:给你个长度为n的钢筋怎么卖最划算? 长度 1 2 3 4 5 6 7 8 9 10 价格 1 5 8 9 10 17 17 20 24 30 现在就是要把所有的切法都遍历一遍,找出最划算的切法,当你把钢筋切了一刀后,是不是变成了两段?那就要考虑的就是这两段怎么切最划算,这样会比较复杂,不妨我们这样: 把钢筋切一刀,成两半,但左边那一半不在考虑,直接按价格卖出,只考虑右边那一半,这样是不是比较简单?当然为了遍历所有切法,左边那部分是从1到n递增的。然而现在我们现在的问题就是变成切一条钢筋,左边不动,把右边又看成一条独立的钢筋切,左边不动,把右边....是不是有递归的味道了?像这样一直重复的解决同一种问题(把右边的钢筋独立看成独立的一条钢筋,继续切)就叫做子问题重叠。 至于什么是最优子结构?顾名思义就是子问题的最优解就是问题的最优解。什么意思?就是如果把钢筋分成A和B两段,那么A的最优解和B的最优解合起来就是原问题的最优解.. 有人说这不是废话么?来看一个例子 现在有一个正方形,顶点为a,b,c,d,要求a->c的最短路径,是不是可以分成a->b和b->c两个子问题?a-

动态规划:LIS

雨燕双飞 提交于 2020-01-20 06:42:04
题目中的严格二字,表示的意思是不允许≥或者是≤的情况出现,只允许>的情况以及<的情况 经典问题是NOIP合唱队形,在这个题目中,既求了最长上升子序列,也求了最长下降子序列 其最终的结果由两个子序列的结果共同得来 我们给出实现方法: //最长上升子序列     for(int i=1;i<=n;i++)     for(int j=i-1;j>=0;j--)     if(a[j]<a[i])       f1[i]=max(f1[i],f1[j]+1); //最长下降子序列     for(int i=n;i>=1;i--)     for(int j=i+1;j<=n+1;j++)     if(a[j]<a[i])       f2[i]=max(f2[i],f2[j]+1); 以最长上升子序列为例,其转移方程为:f(i)=max(f(i),f(j)+1),并且当a[i]>a[j]时进行转移 在实现的时候,一定要控制好下标,以及边界处理,以上代码的边界处理是没有问题的 下面给出合唱队形这道题的代码: 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=105; 5 int n; 6 int ans; 7 int a[maxn]; 8 int f1[maxn],f2

动态规划---LIS

谁说胖子不能爱 提交于 2020-01-20 05:38:29
LIS-- 最长非降子序列 (讲DP基本都会讲到的一个问题LIS:longest increasing subsequence) 题目详情: 一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列的长度。 举个实际的例子来说,对于这个序列:5,3,4,8,6,7,求出其最长非降子序列的长度。 根据动态规划的基本思想,分析问题的状态及其状态转移方程, 假设d(i) = j,i表示取序列的前i个数,j表示这前i个数字的最长非降序列的长度, 首先我们来对前面几个数作分析:d(1) = 1,这个毋庸置疑的,前一个数的LIS就是1,当i = 2时,看到前两个数是(5, 3)由于3比5要小,所以d(2) = 1;当i = 3时,序列为(5, 3, 4),4比3要大,比5要小,这个时候这个序列的子序列最大的显然为2,所以d(3) = 2;当i = 4时,序列为(5, 3, 4, 8),8比前三个数都要大,但是要取出最长的非降子序列应该是(3, 4, 8)而不是(5, 8),分析到这里,就知道,在构造状态转移方程的时候,等式右边应该是一个比较结果的最大值,那又是哪些的比较结果呢?动态规划一般是要利用前面所得到的结果,这样才不会产生重复计算的问题,减少运算量。因此,参加“比较”的应该就是前面计算的到的结果进行比较,最后我们得到状态转移方程为      d(i) = max{ 1, d

DAG上的动态规划

一个人想着一个人 提交于 2020-01-20 05:38:06
不定终点的最长路: 矩阵嵌套问题: 题目链接: http://acm.nyist.net/JudgeOnline/problem.php?pid=16 题目大意: 如果一个矩形的长和宽都大于另一个矩形,那么说这个矩形可以嵌套另一个矩形,现在有很多个矩形,输入矩形的长宽,求出他们的最大嵌套数。注意矩形的长宽是可以互换的。 题目分析: 这是一道经典的DAG上的动态规划问题,需要把问题转化成DAG上的最长路问题,把每个矩形看成点,如果两个矩形可以嵌套,那么他们建边,这样问题就是求最长路了。 状态的定义是对于第 i 个节点,由他出发最长的距离,根据边转移,如果一个矩形可以装下另一个矩形,那么这个矩形出发的最长距离就是他可以装下的那个矩形的最长距离加一。 递归边界是隐含的,如果一个矩形无法嵌套嵌套的矩阵,那么由他出发的最长距离是 1 ,即只有他本身。 AC代码: #include<bits/stdc++.h> using namespace std; int edge[1123][1123]; int d[1123], n; int dp(int i) { int& ans = d[i]; if (ans > 0) return ans; ans = 1; for (int j = 0; j < n; j++) { if (edge[i][j]) ans = max(ans, dp(j)+1

动态规划--LIS

我只是一个虾纸丫 提交于 2020-01-20 04:30:37
  LIS指一个序列中最长的单调递增(严格)的子序列。有一种较为朴素的O(n^2)的做法,我们不多做赘述,我们讲一种用单带栈和二分来实现的O(logn)的算法   我们从1-n枚举,若该数比栈顶元素大,那么我们就将该数压入栈中。否则我们就在整个栈中二分到一个第一个大于等于它的数,将其用a[i]替换。   考虑为什么这样可行,显然若我们二分到的数不是栈顶元素,就不会对答案产生影响。只有我们改变了栈顶元素,那么相当于此时栈中的元素才构成一个单增的子序列。二分改变非栈顶值,只是为了防止我们将未来可能产生的最优解漏掉 n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) { if(top==0||stk[top]<a[i]) stk[++top]=a[i]; else *lower_bound(stk+1,stk+1+top,a[i])=a[i]; } printf("%d\n",top); 总体代码如下: #include <cstdlib> #include <iostream> #include <fstream> #include <algorithm> #include <cstring> #include <cstdio> #include <queue> using namespace std;

wenbao与动态规划

守給你的承諾、 提交于 2020-01-20 04:05:30
终于要开始了。。。。。。。。 万能的dp ------------------------------------------------------------------------------------------------------------------------------------------------------------------ 动规入门级题目 数字三角形 不多说 http://lx.lanqiao.cn/problem.page?gpid=T312 1 #include <iostream> 2 using namespace std; 3 int a[105][105]; 4 int main(){ 5 int n; 6 scanf("%d", &n); 7 for(int i = 0; i < n; ++i){ 8 for(int j = 0; j <= i; ++j){ 9 scanf("%d", &a[i][j]); 10 } 11 } 12 for(int i = n-2; i >= 0; --i){ 13 for(int j = 0; j <= i; ++j){ 14 a[i][j] = a[i][j] + max(a[i+1][j], a[i+1][j+1]); 15 //cout<<i<<" "<<j<<" "<<a[i][j]

动态规划1

旧街凉风 提交于 2020-01-20 01:26:02
1.计数 2.最大最小值 3.存在性 1.确定状态:确定最优策略最后一步,确定子问题 2.转移方程:根据子问题 3.初始条件和边界情况 4.计算顺序:利用之前的计算结果 常见动态规划类型: 1.坐标型 2.序列型 3.划分型 4.区间型 5.背包型 6.最长序列 7.博弈性 8.综合型 669. Coin Change 思路:属于求最值情况 https://www.lintcode.com/problem/coin-change/description?_from=ladder&&fromId=16 public class Solution { /** * @param coins: a list of integer * @param amount: a total amount of money amount * @return: the fewest number of coins that you need to make up */ public int coinChange(int[] coins, int amount) { // write your code here if(coins==null || coins.length ==0){ return 0; } int[] f = new int[amount+1]; f[0]=0; //从i=1开始迭代

算法设计方法概览

僤鯓⒐⒋嵵緔 提交于 2020-01-19 00:49:21
算法设计方法概览 文章目录 算法设计方法概览 递归算法 什么是递归 定义及分类 直接递归 间接递归 尾递归 使用场景 递归模型 递归算法设计 递归与数学归纳法 第一数学归纳法 第二数学归纳法 递归算法设计的一般步骤 分治算法 分治法概述 使用场景 分治法的求解过程 蛮力法 蛮力法概述 使用场景 回溯法 问题的解空间 概述 种类 什么是回溯法 使用回溯法的一般步骤 分枝限界法 什么是分枝限界法 分枝限界法的设计思想 1. 设计合适的限界函数 2. 组织活结点表 3. 确定最优解的解向量 采用分枝限界法的三个关键问题 贪心法 贪心法概述 贪心法应用约束 贪心选择性质 最优子结构性质 动态规划 动态规划的原理 动态规划求解的基本步骤 动态规划与其他方法的比较 递归算法 什么是递归 定义及分类 直接递归 在定义一个过程或者函数时,出现调用本过程或本函数的成分,称之为递归。如果调用自身,称之为直接递归; 间接递归 若过程或者函数p调用过程或者函数q,而q又调用p,称之为间接递归; 任何间接递归都可以等价地转换为直接递归; 尾递归 如果一个递归过程或递归函数中递归调用语句是最后一条执行语句,则称这种递归为尾递归; 使用场景 可以使用递归解决的问题,应该满足以下三个特点: 需要解决的问题可以转换为一个或多个子问题来求解,而这些子问题的求解方法和原问题完全相同,只是数据规模不同;

算法第三章上级实践报告

混江龙づ霸主 提交于 2020-01-18 05:45:58
1. 实践题目: 编辑距离问题 2. 问题描述 给出两个字符串A和B,要用最少的字符操作将字符串A转换为字符串B。字符操作包括: (1)删除一个字符; (2)插入一个字符; (3)将一个字符改为另一个字符。编辑距离即将字符串A变换为字符串B所用的最少字符操作数,可记为d(A,B),求出给出的两个字符串A和B的编辑距离。 3. 算法描述 定义一个dp[i][j]数组,用于记录将 A串的前i个字符转变为B串的前j个字符 所需的编辑距离,初始化dp[i][0]=i,dp[0][j]为j,表示将A串转变为空串所需的编辑距离,以及将空串转变为B串的编辑距离。dp[i-1][j]表示删除A串中的一个字符,dp[i-1][j]表示添加A串中的一个字符,dp[i-1][j-1]表示修改A串中的一个字符,从A字符串的开头开始遍历,进行动态规划操作,取各操作中的最小值。 代码如下: 1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 int dp[10005][10005]; 5 char str1[2005],str2[2005];//str1表示字符串A,str2表示字符串B 6 int main(){ 7 scanf("%s",str1); 8 scanf("%s",str2); 9 int len1 =