动态规划之分割整数

找到大问题和小问题之间共有的特性,列出一定的状态转移规律,然后设计满足条件的小问题解决方案,最后凭借记忆中的中间值快速求出最终解

分割整数的问题需要我在题目中仔细分析出动态规划的一般规律,否则就会陷入一般的思路,绞尽脑汁想不出来,其实把题目对到了动态规划的一般思路上还是比较容易解决的

1、分割整数的最大乘积

这道题的难点在于分割整数并取得最大乘积没有好的办法去记录小问题中的解,但是其实“分割”这一步就是为了找到一个合适的规律,上源码(注释是重点)

    /**
     * 每一个整数分解都与这个整数减一再去分解有关系,
     * 比如
     * 2=1+1
     * 3=1+1+1
     *  =1+2
     * 4=1+1+1+1
     *  =1+2+1
     *  =1+3
     *  =2+2
     * 那么当计算dp[4]时候,遍历到j=1时候 1+1+1和2+1是在dp[3]中计算过的,仅需再计算个3*1即可
     * 遍历到j=2时候 同样2=1+1也在dp[2]中计算过仅需再计算个2*2即可
     * 所以说这道题的难度在于找到加法分解时候相挨着的整数的共性!
     * @param n
     * @return
     */
    private int integerBreak(int n){
        int[] dp = new int[n + 1];
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i - 1; j++) {
                dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j)));
            }
        }
        return dp[n];
    }

2、按平方数来分割整数

这道题乍一看可以用从大到小的递归去做,但是忽略了一个很严重的问题!比如按照递归的思路12=9+1+1+1,那么output就是4,但是12=4+4+4就是3,所以说还是要动态规划来记住中间每个值得最小的返回值,代码如下:

    private int numSquares(int n){
        int[] dp = new int[n+1];
        List<Integer> list = generateSquareList(n);
        dp[0] = 0;
        /**
         * 遍历存储每一个数的最小组合
         */
        for(int i = 1 ; i <= n ; i++){
            int min = Integer.MAX_VALUE;
//            for (int j = list.size() - 1 ; j >= 0 ; j--){
//                int now = list.get(j);
//                if(now > i)
//                    continue;
//                min = Math.min(min, dp[i - now] + 1);
//            }
            for(int j : list){
                if(j > i)
                    break;
                min = Math.min(min, dp[i - j] + 1);
            }
            dp[i] = min;
        }
        return dp[n];
    }

    private List<Integer> generateSquareList(int n) {
        List<Integer> squareList = new ArrayList<>();
        int square = 1;
        int i = 1;
        while (square <= n){
            squareList.add(square);
            square = (i+1)*(i+1);
            i += 1;
        }
        return squareList;
    }

    private int numSquaresMyWrong(int n){
        /**
         * 我这种算法没有记忆功能
         * 类似于贪心思想,但是没法判断贪心的结果是正确的
         * 要能记忆下来以前的那种情况是最优的
         */
        if(n <= 0) return 0;
        return numSquaresMyWrong(n - ((int) Math.sqrt(n) * (int) Math.sqrt(n))) + 1;
    }

3、分割整数构成字符串

 这道题思路来说相对比较简单,类似于裴波那契数列中的爬楼梯问题,总是1.2,但是呢他是有条件的,即不可以为0也不能大于26,而且注意的是不只是0不可以,00也不可以!

    int[] dp;
    private int numDecodings(String s){
        if(s.length() == 0) return 0;
        if(s.charAt(0) == '0') return 0;
        if(s.length() == 1) {
            if (s.charAt(0) == '0') return 0;
            else return 1;
        }
        dp = new int[s.length()+1];
        Arrays.fill(dp, -1);
        dp[0] = 1;
        return helper(s, s.length());
    }

    private int helper(String s, int n){
        if(dp[n] != -1) return dp[n];
        int sum = 0;
        for(int i = 1 ; i <= 2 ; i++){
            if(i == 1){
                if(s.charAt(n-1) != '0'){
                    sum = sum + helper(s, n - 1);
                }
            }else {

                if(n < 2) break;
                if(s.charAt(n-2) == '0') break;//也不能是00
                if(Integer.parseInt(s.substring(n-2, n)) > 26) break;
                else sum = sum + helper(s, n-2);
            }
        }
        dp[n] = sum;
        return dp[n];
    }

猜你喜欢

转载自www.cnblogs.com/lybnumber6/p/12189489.html