算法-动态规划DP小记

动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得到最终问题的解。

关键点就是找到状态转移方程和初始边界条件,说白了就是要找到“递推公式”和初始值,然后计算时保存每一步中间结果,最后累加判断得到结果。

0.求数组最值

求数组最值方法很多,这里使用动态规划的思想来尝试处理,以便更好地理解DP的思想。为了方便这里假设数组a[i]大小为n,要找n个数当中的最大值。

设dp[i]表示第0...i个数的最大值,dp[i-1]表示第0...i-1个数的最大值,所以求前i个数的最大值时,已经知道前i-1个是的最大值是dp[i-1],那么只需要比较dp[i-1]和第i个数谁大就知道了,即dp[i] = max(dp[-1], a[i])。

  1. public int max(int[] a){
  2. int len = a.length;
  3. int[] dp = new int[len];
  4. dp[0] = a[0];
  5. for(int i=1; i<len; i++){
  6. dp[i] = (dp[i-1] > a[i]) ? dp[i-1] : a[i];
  7. }
  8. return dp[len-1];
  9. }

1.求最大公共子序列长度

给定一个字符串,想要删掉某些字符使得最后剩下的字符构成一个回文串(左右对称的字符串,如abcba),问最少删掉多少个字符可获得一个最长回文串。

  1. /**
  2. * 本题求回文串最大长度就转化为求两个字符串的最长公共子序列(不一定连续)
  3. * 策略:字符串可以看做是字符序列,即字符数组。
  4. * 比如有序列A=a0,a1,a2...an;有序列B=b0,b1,b2,b3...bm;设A序列和B序列的公共子序列为C=c0,c1,c2,c3...ck。
  5. * 设L[][]为公共子序列C的长度,L[i][j]的i、j分别表示A、B序列的字符下标,L[i][j]含义是A序列a0、a1、a2...ai和B序列b0、b1、b2、
  6. * ...bj的公共子序列的长度。
  7. *
  8. * 1)如果A序列的i字符和B序列的j字符相等,那么就有ck=ai=bj,公共子序列C的长度L[i][j]=L[i-1][j-1]+1。
  9. * 2)如果A序列的i字符和B序列的j字符不相等,若ai != ck则C为a0...ai-1和b0...bj的最长子序列,若bj != ck则C为a0...ai和b0...bj-1的最长子序列,
  10. * 所以此时公共子序列长度为L[i][j] = max(L[i][j-1], L[i-1][j])。
  11. */
  12. public static int lcs(String s){
  13. if (s == null ) {
  14. return -1;
  15. }
  16. String rs = new StringBuilder(s).reverse().toString();
  17. char[] chars1 = s.toCharArray();
  18. char[] chars2 = rs.toCharArray();//获得反序的字符串
  19. int n = chars1.length;
  20. int[][] dp = new int[n+1][n+1];
  21. for (int i = 0; i < n; i++) {
  22. for (int j = 0; j < n; j++) {
  23. if(chars1[i] == chars2[j]){
  24. dp[i][j] = dp[i-1][j-1] + 1;
  25. }else {
  26. dp[i][j] = dp[i][j-1] > dp[i-1][j] ? dp[i][j-1] : dp[i-1][j];
  27. }
  28. }
  29. }
  30. return n - dp[n][n];
  31. }

2.硬币凑钱问题

只有面值为1元、3元、5元的硬币,数量足够。现在要凑够n元,求需要的最少硬币枚数。


  1. /**
  2. *
  3. * @param n 目标总钱数
  4. * @param coins 硬币数组【1,3,5】
  5. * @return 返回凑够n元需要的最少硬币数
  6. */
  7. public static int getLeastCoinAmount(int n, int[] coins){
  8. if (coins == null || n < 0) {
  9. return -1;
  10. }
  11. if (n == 0){
  12. return 0;
  13. }
  14. int[] dp = new int[n+1]; //dp[i]=j表示凑够i元最少需要j枚硬币。数组长度设为(n+1)保证可以访问dp[n]。
  15. dp[0] = 0;
  16. for (int i = 1; i <= n; i++) {
  17. dp[i] = Integer.MAX_VALUE;
  18. }
  19. int coinValue = 0;
  20. for (int i = 1; i <= n; i++) {//问题规模从小到大,直到达到目标面值
  21. for (int j = 0; j < coins.length; j++) {//遍历所有面值的硬币,j表示硬币面值的下标
  22. coinValue = coins[j];
  23. if (i - coinValue >= 0 && 1 + dp[i-coinValue] < dp[i]){ //当前方案的硬币数更少,则使用当前方案
  24. dp[i] = 1 + dp[i-coins[j]];
  25. }
  26. }
  27. }
  28. return dp[n];
  29. }

3.最长非降子序列

一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列的长度。


  1. /**
  2. *
  3. * 定义d(i)表示前i个数中"以A[i]结尾"的最长非降子序列的长度。
  4. * 对序列A1...Ai,找到的最长子序列长度d[i]分两种情况:
  5. * (1)包含最后一个数Ai,即d[i]=max{d[j]+1}(1<=j<i且Aj<=Ai),满足条件的Aj可能会有多个,选最大的d[j],如果Aj都大于Ai则d[j]=0;
  6. * (2)不含最后一个数,即d[i]=d[i-1]
  7. *
  8. * 综上:d[i] = max{d[i-1], max{d[j]+1}}
  9. */
  10. public static int longestIncreasingSubsequence(int[] a){
  11. if (a == null) {
  12. return -1;
  13. }
  14. if (a.length < 1){
  15. return 0;
  16. }
  17. int len = a.length;
  18. int[] dp = new int[len];//dp[i]系统自动初始化为0
  19. dp[0] = 1;
  20. for (int i = 1; i < len; i++) {//迭代,求序列0...len-1的最长子序列长度
  21. for (int j = 0; j < i; j++) {//寻找Ai之前的序列,看是否有不大于Ai的数字Aj
  22. if (a[j] <= a[i] && dp[i] < dp[j] + 1){//假设最长子序列包含最后一个数
  23. dp[i] = dp[j] + 1;
  24. }
  25. }
  26. //寻找Ai之前的序列如果Ai都小于Aj,此时dp[i]并没有被修改仍为初始值0。所以包含最后一个数的最长子序列就只有最后一个数自身,长1
  27. dp[i] = Math.max(1, dp[i]);
  28. //至此,已经求出了包含最后一个数的最长子序列的长度,和不包含最后一个数的最长子序列长度比较,取最大值为当前的最大长度
  29. dp[i] = Math.max(dp[i], dp[i-1]);
  30. }
  31. return dp[len-1];
  32. }

4.经典01背包问题

01背包问题:一个承重(或体积)为W的背包,可选物品有n个,第i个物品分别重w[i]和价值v[i],每个物品只能拿或不拿,求背包可放物品的最大价值。


  1. /**
  2. *
  3. * 策略:这里的关键制约因素是背包只能承重w,而且每放入一个物品其承重就会减少。
  4. * 因此定义maxValue=V[i][j],数组表示目前可选物品有i个:0、1...i-1,背包承重(剩余的存放重量)为j的最大价值。
  5. * 现在假设已经知道了(i-1)个物品且剩余承重为j的最大价值V[i-1][j],那么考虑准备放入第i个物品的情况:
  6. * (1)如果第i个物品的重量大于背包的剩余承重w_i>j,显然放不下了,所以此时V[i][j]=V[i-1][j];
  7. * (2)w_i<=j,显然可以放下第i个物品,物品可以放得下,但是一定要装进来吗?如果装进的物品价值较低且较重,无疑会影响后续物品的装入情况。
  8. * 所以还要考虑要不要放进来的子问题,V[i][j]=max{vi+V[i-1][j-wi], V[i-1][j]}。
  9. *
  10. * @param W
  11. * @param n
  12. * @param w
  13. * @param v
  14. * @return
  15. */
  16. public static int knapsack(int W, int n, int[] w, int[] v){
  17. if ( W < 1 || n < 1 || w == null || v == null) {
  18. return -1;
  19. }
  20. int[][] dp = new int[n+1][W+1]; //可选的物品最多可以有n个,所以行数设为n+1。最大承重是W,所以列设为W+1。
  21. int index = 0;
  22. for (int i = 1; i <= n; i++) { //物品数肯定是从1开始。dp[0][j]系统初始化为0.
  23. index = i-1;
  24. for (int j = 1; j <= W ; j++) {//能装进的重量肯定是从1开始。dp[i][0]系统初始化为0.
  25. if (w[index] > j){
  26. dp[i][j] = dp[i-1][j];
  27. }else {
  28. dp[i][j] = Math.max(dp[i - 1][j - w[index]] + v[index], dp[i - 1][j]);
  29. }
  30. }
  31. }
  32. //找出是哪些物品放入背包
  33. boolean[] isTaken = new boolean[n];//标记是否放入背包里
  34. for (int i = n; i > 0 ; i--) {
  35. if (dp[i][W] != dp[i-1][W]){
  36. isTaken[i-1] = true;//装入
  37. W -= w[i-1];//装入之后背包的承重减少
  38. System.out.println(i-1);
  39. }
  40. }
  41. return dp[n][W];//返回n个物品承重为W时的最大价值
  42. }
  43. }

推荐文章:

动态规划:从新手到专家

动态规划解决01背包问题(java实现)

算法-动态规划DP小记的更多相关文章

  1. 【转】动态规划DP

    [数据结构与算法] DP 动态规划 介绍 原创 2017年02月13日 00:42:51 最近在看算法导论. DP全称是dynamic programming,这里programming不是编程,是一 ...

  2. 动态规划dp

    一.概念:动态规划dp:是一种分阶段求解决策问题的数学思想. 总结起来就一句话:大事化小,小事化了 二.例子 1.走台阶问题 F(10):10级台阶的走法数量 所以:F(10)=F(9)+F(8) F ...

  3. LeetCode探索初级算法 - 动态规划

    LeetCode探索初级算法 - 动态规划 今天在LeetCode上做了几个简单的动态规划的题目,也算是对动态规划有个基本的了解了.现在对动态规划这个算法做一个简单的总结. 什么是动态规划 动态规划英 ...

  4. HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)

    /** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...

  5. 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

    算法-动态规划 Dynamic Programming--从菜鸟到老鸟      版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/u013309870/ar ...

  6. 算法之DP

    一般DP 都是有模板的,先初始化,然后找到不同状态下数值的关系,使得某个状态可用另一个状态由一个固定的方式转移而来,列出状态转移方程,这就是DP: 例题 P1216 [USACO1.5]数字三角形 N ...

  7. LeetCode初级算法--动态规划01:爬楼梯

    LeetCode初级算法--动态规划01:爬楼梯 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net ...

  8. 算法-数位dp

    算法-数位dp 前置知识: \(\texttt{dp}\) \(\texttt{Dfs}\) 参考文献 https://www.cnblogs.com/y2823774827y/p/10301145. ...

  9. 动态规划——DP算法(Dynamic Programing)

    一.斐波那契数列(递归VS动态规划) 1.斐波那契数列——递归实现(python语言)——自顶向下 递归调用是非常耗费内存的,程序虽然简洁可是算法复杂度为O(2^n),当n很大时,程序运行很慢,甚至内 ...

随机推荐

  1. frameset的各个frame之间互相访问的方法

    工作中很少使用到frameset,对其了解也是十分有限,这里在网上找了点资料,摘抄了部分内容. (1)获得html页面上的frame window.frames可以获得本页面上所有frame集合,用法 ...

  2. C#_父窗体跟子窗体的控件操作

    很多人都苦恼于如何在子窗体中操作主窗体上的控件,或者在主窗体中操作子窗体上的控件.相比较而言,后面稍微简单一些,只要在主窗体中创建子窗体的时候,保留所创建子窗体对象即可. 下面重点介绍前一种,目前常见 ...

  3. photoshop cs6安装过程中安装程序遇到错误:请重启计算机,解决办法

    1.关闭防火墙和杀毒软件 2.删除注册表 依次展开HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager目录,找到其中的 ...

  4. Linux_02

    1.vim编辑器 vim操作命令 --在命令模式下进行 pageup 往上翻页 pagedown 往下翻页 H 移动到屏幕首行 gg 移动光标到文档的首行 前面加数字n表示移动到n行内容 G 移动到文 ...

  5. 一篇带你读懂TCP之“滑动窗口”协议

    前言 你现在的努力,是为了以后有更多的选择. 在上一篇文章通过"表白"方式,让我们快速了解网络七层协议了解了网络七层协议. 接下来我们要把重心放在网络传输的可靠性上面.一起来看TC ...

  6. SqlHelper DBHelper

    根据自己项目的开发需要,整理了一个SqlHelper类 相比较网上通用的SqlHelper类方法主要有一下几点的不同: 1.因为要操作多个数据库,所以数据库连接字符串没有写死到方法里,作为参数提供出来 ...

  7. 在CentOS上搭建PHP服务器环境(可用)

    原文:https://www.cnblogs.com/zy2009/p/7047828.html 1,先安装apache: yum install httpd 配置ServerName vi /etc ...

  8. 20135202闫佳歆--week4 系统调用(上)--学习笔记

    此为个人笔记存档 week 4 系统调用(上) 一.用户态.内核态和中断处理过程 用户通过库函数与系统调用联系起来. 1.内核态 在高执行级别下,代码可以执行特权指令,访问任意的物理地址. 2.用户态 ...

  9. Yale数据库上的人脸识别

    一.问题分析 1. 问题描述 在Yale数据集上完成以下工作:在给定的人脸库中,通过算法完成人脸识别,算法需要做到能判断出测试的人脸是否属于给定的数据集.如果属于,需要判断出测试的人脸属于数据集中的哪 ...

  10. PHP 执行命令时sudo权限的配置

    PHP 执行命令时sudo权限的配置 1.先写一个PHP文件 <?php system('whoami'); 先看自己的apache2的用户是谁,下面是笔者的截图,笔者使用apche2的用户是w ...