问题描述
设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.txt | output.txt | 
|---|---|
| 4 3 | 144 | 
| 1234 | 
思路分析
1. 引导
假如按照上述input.txt文件所示,我要求出4位整数1234划分成3段的最大乘积。
那么根据划分子问题的原则:
上述最大乘积是不是等价于:
此时,我们再取第一种情况 3位整数123划分成2段的最大乘积 来进行子问题的划分:
 3位整数123划分成2段的最大乘积 等价于:
划分到这里我们就已经能够直接求解了,因为划分成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 \ i | 1 | 2 | 3 | 4 | 
|---|---|---|---|---|
| 1 | 1 | 12 | 123 | 1234 | 
| 2 | 0 | 2 | 36 | 492 | 
| 3 | 0 | 0 | 6 | 144 | 
4. 状态转移
- 首先用一个二重循环遍历整个dp数组,外层是 i,内层是k
- 如果内层大于外层,说明分段数比位数还大,这是不可能的,直接dp[ j ][ i ] = 0即可
- 如果内层小于外层,说明符合条件,此时再来一层循环,从当前的列 i 开始,一直到1,具体的过程见 1. 引导 部分
- 值得注意的是,这里的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;
    }
}










