蓝桥杯 算法训练 ALGO-116 最大的算式

算法训练 最大的算式  
时间限制:1.0s   内存限制:256.0MB
问题描述
  题目很简单,给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果尽量大。因为乘号和加号一共就是N-1个了,所以恰好每两个相邻数字之间都有一个符号。例如:
  N=5,K=2,5个数字分别为1、2、3、4、5,可以加成:
  1*2*(3+4+5)=24
  1*(2+3)*(4+5)=45
  (1*2+3)*(4+5)=45
  ……
输入格式
  输入文件共有二行,第一行为两个有空格隔开的整数,表示N和K,其中(2<=N<=15, 0<=K<=N-1)。第二行为 N个用空格隔开的数字(每个数字在0到9之间)。
输出格式
  输出文件仅一行包含一个整数,表示要求的最大的结果
样例输入
5 2
1 2 3 4 5
样例输出
120
样例说明
  (1+2+3)*4*5=120
 

题目解析:

  本题涉及到一种算法——动态规划。

  (1)动态规划思想

  在分治求解过程中,有些子问题被重复计算了许多次。如果能够保存已解决的子问题的答案,而在需要时再找出,就可以避免大量重复问题的计算,从而得到多项式时间算法。
  (2)设计动态规划的步骤 
      ① 找出最优解的性质,并刻画其结构特征;
      ② 递归地定义最优值(写出动态规划方程);
      ③ 以自底向上的方式计算出最优值(填入表格);
      ④ 根据计算最优值时得到的信息,构造一个最优解。
    说明:  a.步骤 ① ~ ③ 是动态规划算法的基本步骤;
         b.在只需要求出最优值的情况,步骤 ④ 可以省略;若需要求出一个最优解,则必须要有第 ④ 步。
  (3)动态规划的特征
    ① 最优子结构
        当问题的最优解包含了其子问题的最优解,称该问题具有最优子结构性质。
    ② 重叠子问题
        在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重要性质,对每一个子问题只求        解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题。
 
  以题目给出的样例输入为例,分析动态规划算法:
  (1)利用 sum 数组将前 i 个数之和保存。
    
  (2)利用 dp 数组来保存前 i 个数有 0 个乘号时的最大值(全加时的值,与 sum 数组相同),即 dp[i][0];
    
  (3)在动态规划算法中,从第二( i 从 2 开始)个数后开始加乘号,前 i 个数循环累加 i - 1 (j 从 1 开始,到 i -1 结束,且不能大于 k)个乘号,乘号位置循环从第一个数后的位置到第 i 个数前的位置(p 从 2 开始, 到 i 结束);
   step 1:  i = 2    j = 1    p = 2     说明:前两个数,有一个乘号,位置在第二个数前面
        dp[2][1] = 0                说明:前两个数一个乘号时,值为 0 (表1.2中 dp[2][1])
        dp[1][0] x ( sum[2] - sum[1] ) = 2  说明:前一个数没有乘号乘上前两个数之和减去前一个数之和,即前一个数乘第二个数(1*2 = 2)
        dp[2][1] = max( 0 , 2 )         说明:填入 dp 表中
        
   step 2:  i = 3    j = 1    p = 2     说明:前三个数,有一个乘号,位置在第二个数前面
        dp[3][1] = 0                说明:前三个数一个乘号时,值为 0 (表1.2中 dp[3][1])
        dp[1][0] x ( sum[3] - sum[1] ) = 2  说明:前一个数没有乘号乘上前三个数之和减去前一个数之和,即前一个数乘前两个数之和(1*(2+3) = 5)
        dp[3][1] = max( 0 , 5 )         说明:填入 dp 表中
         
 
   step 3:  i = 3    j = 1    p = 3     说明:前三个数,有一个乘号,位置在第三个数前面
        dp[3][1] = 5                说明:前三个数一个乘号时,值为 0 (表1.2中 dp[3][1])
        dp[2][0] x ( sum[3] - sum[2] ) = 9  说明:前两个数没有乘号乘上前三个数之和减去前两个数之和,即前一个数乘第三个数之和((1+2)* 3) = 9)
        dp[3][1] = max( 5 , 9 )         说明:填入 dp 表中
        
   . . . . . .
   只到所有的循环执行结束,一共 19 步。dp 表最终结果为:
        
   当 5 个数有 2 个乘号时,最大值应为 dp[5][2] = 120。在循环执行过程中,我们不用担心 dp[p-1][j-1] * (sum[i] - sum[p-1]) 究竟是那几个数得到的结果,而使用它的值就可以啦,这就是动态规划最重要的特性之一!
 
示例代码:
 1 import java.io.BufferedReader;
 2 import java.io.IOException;
 3 import java.io.InputStreamReader;
 4 
 5 public class Main {
 6     public static void main(String[] args) throws IOException {
 7         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 8         String[] str = br.readLine().split(" ");
 9         int n = Integer.parseInt(str[0]);
10         int k = Integer.parseInt(str[1]);
11         
12         long[][] dp = new long[n+1][k+1];    //dp[i][j]表示前i个数中有j个乘号时,所得最大值
13         int[] sum = new int[n+1];        //前i个数之和
14         
15         str = br.readLine().split(" ");
16         for(int i = 1; i <= n; i++) {
17             sum[i] =  sum[i-1] + Integer.parseInt(str[i-1]);
18         }
19         
20         //没有乘号的情况,即连加的情况
21         for(int i = 1; i <= n; i++) {
22             dp[i][0] = sum[i];
23         }
24         //动态规划
25         for(int i = 2; i <= n; i++) {                    //前i个数
26             for(int j = 1; j <= i-1 && j <= k; j++) {    //乘号的个数
27                 for(int p = 2; p <= i; p++) {            //乘号的位置
28                     dp[i][j] = max(dp[i][j], dp[p-1][j-1] * (sum[i] - sum[p-1]));//求前i个数有j个乘号时的最大值
29                 }
30             }
31         }
32         
33         System.out.println(dp[n][k]);
34     }
35 
36     /**
37      * 求最大数
38      * @param a    参数1
39      * @param b    参数2
40      * @return    a b中的最大数
41      */
42     private static long max(long a, long b) {
43         return a>b?a:b;
44     }
45 }
posted @ 2017-04-11 20:41  C3Stones  阅读(2020)  评论(0编辑  收藏  举报