藍橋杯 算法訓練 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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM