『动态规划』最大K乘积问题

问题描述

设I是一个n位十进制整数。如果将I划分为k段,则可得到k个整数。这k个整数的乘积称为I的一个k乘积。试设计一个算法,对于给定的I和k,求出I的最大k乘积。

例如十进制整数 1234 划分为 3 段可有如下情形:
1 × 2 × 34 = 68
1 × 23 × 4 = 92
12 × 3 × 4 = 144

编程任务

对于给定的I 和k,编程计算I 的最大k 乘积。

数据输入

输入的第1 行中有2个正整数n和k。
正整数n是序列的长度;正整数k是分割的段数。接下来的一行中是一个n位十进制整数。(n<=10)

结果输出

input.txtoutput.txt
4 3144
1234

思路分析

1. 引导

假如按照上述input.txt文件所示,我要求出4位整数1234划分成3段的最大乘积。

那么根据划分子问题的原则

上述最大乘积是不是等价于:

max {
3位整数123划分成2段的最大乘积 x 4 ,
2位整数12划分成2段的最大乘积 x 34,
1位整数1划分成2段的最大乘积 x 234
}

此时,我们再取第一种情况 3位整数123划分成2段的最大乘积 来进行子问题的划分:
3位整数123划分成2段的最大乘积 等价于:

max {
2位整数12划分成1段的最大乘积 x 3,
1位整数1划分成1段的最大乘积 x 23
}

划分到这里我们就已经能够直接求解了,因为划分成1段就是最基本的子问题,此时的max = 36

2. 选择正确的数据结构

从上面可以看出,我们需要把整数1234的任意连续子段的数值给求出来,例如12,23,34,234

这首先要求我们把每位数字分隔到一个数组中。

因此有了下面的两个函数:

// 把数值分隔放入数组中,num表示数字,n表示位数
    public static void putNumberIntoArray(int number[], int num, int n ) {
        int temp = 0;
        for(int i = n; i > 0; i--) {
            temp = num % 10;
            num = num / 10;
            number[i] = temp;
        }
    }
// 求一个数中,从 第m位 到 第n位的数值大小 (1 <= m <= n <= length)
    public  static int getSubsequenceNumber(int m, int n, int number[]) {
        int subsequenceNumber = 0;
        for(int i = m ; i <= n ; i++) {
            subsequenceNumber = subsequenceNumber * 10 + number[i];
        }
        return subsequenceNumber;
    }

3. 定义dp数组

定义 dp[ k ] [ i ]

表示将文件中给定的整数num的前 i 位划分为 k 段的最大k乘积。

k \ i1234
11121231234
20236492
3006144

4. 状态转移

  1. 首先用一个二重循环遍历整个dp数组,外层是 i,内层是k
  2. 如果内层大于外层,说明分段数比位数还大,这是不可能的,直接dp[ j ][ i ] = 0即可
  3. 如果内层小于外层,说明符合条件,此时再来一层循环,从当前的列 i 开始,一直到1,具体的过程见 1. 引导 部分
  4. 值得注意的是,这里的dp数组的下标要格外注意,分别是j - 1 和 w - 1,其中 j -1代表去掉后面的数字之后,前面的自然是分为k-1段;w - 1代表后面数字的前面数字部分的位数

完整代码

package algorithm;

import java.io.*;

// 最大k乘积问题 动态规划
public class experiment02 {
    public static void main(String[] args) {
        int []number = new int[10]; // 存放数字的数组
        int [][]dp = new int[10][10]; // 存放计算结果的dp二维数组
        String [][]arr = new String[2][2]; // 保存输入文件中数据的二维数组

        String srcPath = "Input.txt";
        String targetPath = "Output.txt";

        try {
            FileReader fr = new FileReader(srcPath);
            BufferedReader bfr = new BufferedReader(fr);
            String c = null;
            int cnt = 0;
            while ((c = bfr.readLine()) != null) {
                arr[cnt++] = c.split(" ");
            }
            fr.close();
            bfr.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }

        int num = Integer.parseInt(arr[1][0]); // 文件中的数字
        int n = Integer.parseInt((arr[0][0])); // 数字的位数
        int k = Integer.parseInt(arr[0][1]); // 分割成k段

        // 把文件中的数字分开 放入数组中
        putNumberIntoArray(number, num, n);

        // base case
        if(k == 1) {
            System.out.println(num);
        }

        // 初始化
        for(int i = 1 ; i <= n ; i++) {
            dp[1][i] = getSubsequenceNumber(1,i,number);
        }
//        for(int i = 1 ; i <= n ; i++) System.out.println(dp[1][i]);

        // 状态转移
        for(int i = 1 ; i <= n ; i++) { // i表示列 意思是 n位整数的前i位表示的数字 比如123 ,i = 2.那就是12
            for(int j = 2 ; j <= k ; j++) { // j表示行
                if(j > i) { // 把n位数分成 >n 的片段,是不可能的情况,break
                    dp[j][i] = 0;
                    break;
                } else {
                    int max = 0;
                    int temp = 0;
                    for(int w = i ; w > 0 ; w--) {
                        temp = dp[j-1][w-1] * getSubsequenceNumber(w , i, number);
                        max = Math.max(temp,max);
//                        System.out.println(temp);
                    }
                    dp[j][i] = max;
                }
            }
        }

        try {
            FileWriter fw = new FileWriter(targetPath);
            BufferedWriter bfw = new BufferedWriter(fw);
            bfw.write(Double.toString(dp[k][n])); // int 写不进去 double不适用 因此只能转成字符串才能写进去
            bfw.flush();
            bfw.close();
            fw.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }

        for(int i = 1 ; i <= k ; i++) {
            for( int j = 1 ; j <= n ; j++) {
                System.out.print(dp[i][j] + " ");
            }
            System.out.println();
        }

        System.out.println("----------------");
        System.out.println("将" + num + "分成" + k + "段,最大k乘积为:" + dp[k][n]);
    }

    // 把数值分隔放入数组中,num表示数字,n表示位数
    public static void putNumberIntoArray(int number[], int num, int n ) {
        int temp = 0;
        for(int i = n; i > 0; i--) {
            temp = num % 10;
            num = num / 10;
            number[i] = temp;
        }
    }

    // 求一个数中,从 第m位 到 第n位的数值大小 (1 <= m <= n <= length)
    public  static int getSubsequenceNumber(int m, int n, int number[]) {
        int subsequenceNumber = 0;
        for(int i = m ; i <= n ; i++) {
            subsequenceNumber = subsequenceNumber * 10 + number[i];
        }
        return subsequenceNumber;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值