动态规划学习之LeetCode分割整数相关题目(第343、279、91题)

题目:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 不小于 2 且不大于 58。

分析:

  1.定义一个状态转移数组dp,dp[i]表示数字i拆分为至少两项之和后,拆分后的数字之间的最大乘积。

  2.寻找关系式,那么如何确定dp[i]的最大值呢?首先我们要确定它有哪些取值的可能,对于拆分之和的数字的乘积,这个乘积可能是两个数字的乘积,也可能是多个数字之间的乘积,那么如何表示这两种乘积呢?

    1)对于两个数的乘积,就是一个小于i的数j,和i-j之间的乘积,即 (i - j)* j;

    2)对于多个数之间的乘积,就是相当于一个小于i的数j,和dp[i-j]的乘积,即 j * dp[i-j] ;

  3.初始条件,根据题目,n从2开始,多以dp[2] = 1;

  4.需要借助于双层循环,外层循环从3开始,到n结束,因此状态转移数组的空间大小是n+1,内层循环是为了寻找当前i的最大拆分数字之积

首先的代码如下:

    public int integerBreak(int n) {
        if (n==2)
            return 1;
        int[] dp = new int[n+1];
        dp[2] = 1;

        for (int i = 3; i <= n ; i++) {
            for (int j = 1; j < i; j++) {
                dp[i] = Math.max(Math.max(dp[i],j*(i-j)),j*dp[i-j]);
            }
        }
        return dp[n];
    }

题目:给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例:

输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.

分析:这个题的解决思路和上面那个题的解决思路相似,具体流程如下:

  1.定义一个状态转移数组dp,dp[i]表示数字i由完全平方数组成的最少个数;

  2.寻找关系式,如果i本身就是一个完全平方数,那么这就是最理想的情况了,dp[i]的值直接为1;如果不是,那么就需要从小于1~i-1的数字中去寻找了,那么此时dp[i] = dp[j] + dp[i-j],即i拆分为j和i-j,二者的最少个数,就是i的最少个数。

  3.初始值,外层循环是从1开始遍历,到n结束,因此dp[1] = 1,内层循环用于寻找当前遍历的数字i的拥有完全平方数的最少个数,如果i不是完全平方数字,那么在i自己寻找之前,dp[i]设置为一个最大值。

实现代码:

    public int numSquares(int n) {
        if ((int) Math.sqrt(n) - Math.sqrt(n) == 0){
            return 1;
        }
        int[] dp = new int[n+1];
        dp[1] = 1;

        for (int i = 2; i <= n; i++) {
            if ((int) Math.sqrt(i) - Math.sqrt(i) == 0){
                dp[i] = 1;
            }else {
                dp[i] = Integer.MAX_VALUE;
                for (int j = 1; j < i; j++) {
                    dp[i] = Math.min(dp[i],dp[j] + dp[i-j]);
                }
            }
        }
        return dp[n];
    }

题目:一条包含字母 A-Z 的消息通过以下方式进行了编码:

‘A‘ -> 1
‘B‘ -> 2
...
‘Z‘ -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。

示例:

输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)

分析:对于给定的一串数字,能够正确的解码的前提条件是,这个数字或者它和它前面的一个数字组成的两位数字位于1~26之间,否则都是不能被正确解码的,具体的过程如下:

  1.定义一个数组dp,dp[i]表示前i个字符构成的子串所拥有的解码方法的总数;

  2.寻找关系式:对于能够解码的情况有两种:

    一种是,对于当前遍历的这个字符,被用于单独解码,它只要大于‘0‘,那么就能够被解码,在它被用来单独解码的时候,它的前i-1个字符构成的子串的解码方法总数是不变的,也就是相当于在前i-1个字符构成的子串末尾增加一个单独编码的字符,并不会影响整体编码,它们的解码方法总数一致;

    另一种是,对于当前遍历的这个字符,不是被用于单独编码的,它被用来和它的前一个字符组成一个二位数,如果这个二位数在10~26之间,对于这种的组合编码的情况下,前i个字符构成的子串含有的解码方法总数是和它的前i-2个字符构成的子串的解码方法总数是一致的,即在前i-2个字符构成的子串的末尾加上一个组合编码数,并不会影响前i-2字符的解码情况。

    这两个情况需要逐一判断,才不会漏掉所有的解码方法

  3.初始值,dp[0] = 1,dp[1] = 1,这里d[1]代表字符串的第一个字符,dp[0]没有实际的含义,只是充当一个数字1存在,此处设置dp[0]为1,纯粹是为了i=1时:

  第i个字符和第i-1个字符能够构成合法解码数字的时候,使解码方法总数加1。因为此时子串的总个数是2,比如226,子串22解码个数包括2、2和22。为22的时候,相对于dp[2]的值,应该是dp[1]的值再加上1,因为22也构成了一个解码方法,而dp[0]并没有对应的子字符串,所以用来当做一个1。

实现代码:

    public int numDecodings(String s) {
        if (s.charAt(0)==‘0‘)
            return 0;
        if (s.length()==1&&s.charAt(0) > ‘0‘)
            return 1;
        int n = s.length();
        int[] dp = new int[s.length()+1];
        dp[0] = 1;
        dp[1] = 1;
        for (int i = 1; i < n; i++) {
            int two_nums = s.charAt(i) - ‘0‘ + (s.charAt(i-1) -‘0‘)*10;
            if (s.charAt(i) > ‘0‘)
                dp[i+1] = dp[i];
            if (two_nums > 9 && two_nums <=26)
                dp[i+1] += dp[i-1];
            if (dp[i+1]==0)
                return 0;
        }
        return dp[n];
    }

一定要根据题目的要求找清楚数组元素之间的推导关系!!!

原文地址:https://www.cnblogs.com/yxym2016/p/12641507.html

时间: 2024-08-29 15:18:15

动态规划学习之LeetCode分割整数相关题目(第343、279、91题)的相关文章

leetcode 单链表相关题目汇总

  leetcode-19-Remove Nth From End of List—移除链表中倒数第n个元素 leetcode-21-Merge Two Sorted Lists—两个已排序链表归并 leetcode-23-Merge k Sorted Lists—k个已排序链表归并 leetcode-24-Swap Nodes in Pairs—链表中元素两两一组交换 leetcode-25-Reverse Nodes in K-Group—链表中元素k个一组逆序 leetcode-61-Ro

LeetCode: Palindrome 回文相关题目

LeetCode: Palindrome 回文相关题目汇总 LeetCode: Palindrome Partitioning 解题报告 LeetCode: Palindrome Partitioning II 解题报告 Leetcode:[DP]Longest Palindromic Substring 解题报告 LeetCode: Valid Palindrome 解题报告

动态规划学习笔记(不定期更新)

最近刚开始接触动态规划(Dynamic Programming)算法,之前略有耳闻,一直觉得DP非常之高大上,看了某些题的DP解法也是云里雾里,哇擦?!这么几行代码就解决了?怎么全是数组操作?时间复杂度也很低的样子.其实不然,当我真正开始学习动态规划的时候才发现这货没那么玄乎. 把我对DP浅显的理解总结为以下几点: 1.空间换时间. 2.找到状态. 3.找到状态转移方程. 动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决.为了能够快速的不重复的计

LeetCode:二叉树相关应用

LeetCode:二叉树相关应用 基础知识 617.归并两个二叉树 题目 Given two binary trees and imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge them into a new binary tree. The merge ru

OpenCV学习(20) grabcut分割算法

http://www.cnblogs.com/mikewolf2002/p/3330390.html OpenCV学习(20) grabcut分割算法 在OpenCV中,实现了grabcut分割算法,该算法可以方便的分割出前景图像,操作简单,而且分割的效果很好.算法的原理参见papaer:“GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts 比如下面的一副图,我们只要选定一个四边形框,把框中的图像作为gra

Linux学习记录--文件IO操作相关系统编程

文件IO操作相关系统编程 这里主要说两套IO操作接口,分别是: POSIX标准 read|write接口,函数定义在#include<unistd.h> ISO C标准 fread|fwrite接口,函数定义在#include<stdio.h> 有书上说POSIX标准与ISO C标准的区别在于文件读写是否带缓冲区,我则不是很认同,因此POSIX标准下的IO操作也是带缓冲区的,至于这两个标准下的IO性能谁更加好则不一定,因为这和缓冲区的大小,以及用户逻辑有很大关系. POSIX标准

九度 Online Judge 之《剑指 Offer》一书相关题目解答

前段时间准备华为机试,正好之前看了一遍<剑指 Offer>,就在九度 Online Judge 上刷了书中的题目,使用的语言为 C++:只有3题没做,其他的都做了. 正如 Linus Torvalds 所言“Talk is cheap, show me the code!",详见托管在 Github 的源码:点击打开链接(核心部分有注释). 难度总结:有些可以不必要像书中那样保存中间结果,直接输出,也就可以不使用书中的算法:有些题目在书中题目的基础上加了更多的要求,也更难实现一点.

动态规划学习系列——划分DP(三)

划分DP第三题,wikioi 1040,送我n个WA~~~ 题目大意: 这道题题述有着UVA的特色,够废话,其实就是读入一个长度最大200的字符串(不知道为何要分行输入,完全没有意义啊),分成m部分,使各部分单词量加起来最大 解题思路: 这题划分的部分跟乘积最大那题其实很像,状态转移方程也很容易想到: dp[i][k]=max(dp[i][k],dp[j][k-1]+scnt[j+1][i]) ( j >= k-1 ) scnt数组:scnt[j][i] 表示区间 j~i 所包含的单次总数 接下

hdu 2079 选课时间(题目已修改,注意读题) 多重背包

选课时间(题目已修改,注意读题) Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3162    Accepted Submission(s): 2472 Problem Description 又到了选课的时间了,xhd看着选课表发呆,为了想让下一学期好过点,他想知道学n个学分共有多少组合.你来帮帮他吧.(xhd认为一样学分的课没区别