ALGO-17_藍橋杯_算法訓練_乘積最大(DP)


問題描述 

  今年是國際數學聯盟確定的“2000——世界數學年”,又恰逢我國著名數學家華羅庚先生誕辰90周年。在華羅庚先生的家鄉江蘇金壇,組織了一場別開生面的數學智力競賽的活動,你的一個好朋友XZ也有幸得以參加。活動中,主持人給所有參加活動的選手出了這樣一道題目:

  設有一個長度為N的數字串,要求選手使用K個乘號將它分成K+1個部分,找出一種分法,使得這K+1個部分的乘積能夠為最大。

  同時,為了幫助選手能夠正確理解題意,主持人還舉了如下的一個例子:

  有一個數字串:312, 當N=3,K=1時會有以下兩種分法:

  3*12=36
  31*2=62

  這時,符合題目要求的結果是:31*2=62

  現在,請你幫助你的好朋友XZ設計一個程序,求得正確的答案。

輸入格式 

  程序的輸入共有兩行:
  第一行共有2個自然數N,K(6≤N≤401≤K≤6)
  第二行是一個長度為N的數字串。


輸出格式 

  輸出所求得的最大乘積(一個自然數)。

  樣例輸入

  4 2
  1231
樣例輸出
62

 

記:

由於DP題目的解法仍不熟悉,故上網搜尋解題思路

(代碼參考:https://blog.csdn.net/fine_rose/article/details/63685548)

 參考代碼:

 1 #include <stdio.h> 
 2 #define MIN(X,Y) (X)<(Y)?(X):(Y)
 3 
 4 int n,k;
 5 long long num[45] = {0};
 6 long long dp[45][6] = {0};/*可使用的數字,*號的個數*/
 7 
 8 long long cut(int l,int r)
 9 {
10     int i;
11     long long end = 0;
12     for (i = l ; i <= r ;i ++)
13     {
14         end = end*10 + num[i];        
15     }
16     return end;
17 }
18 
19 void init()
20 {
21     int i;
22     char tmp[45] = {0};
23     scanf("%d %d",&n,&k);
24     scanf("%s",&tmp);
25     for (i = 1 ; i <= n ; i ++)
26     {
27         num[i] = tmp[i-1]-'0';
28     }
29     for (i = 1 ; i <= n ; i ++)
30     {
31         dp[i][0] = cut(1,i);
32     }
33     return ;
34 }
35 
36 void find()
37 {
38     int i,j;
39     int a,b;
40         
41     for (i = 2 ; i <= n ; i ++)/*枚舉能使用數字(1-i),(至少需要2數字才可添加*號)*/
42     {
43         j = MIN(i-1,k);/*枚舉在能使用數字中最多能放的*個數*/
44         for (a = 1 ; a <= j ; a ++)/*枚舉j個乘號情況下的數字分布*/
45         {
46             for (b = a ; b < i ; b ++)/*枚舉在能使用數字內的不同組合*/
47             {
48                 if (dp[b][a-1]*cut(b+1,i) > dp[i][a])
49                 {
50                     dp[i][a] = dp[b][a-1]*cut(b+1,i);/*存放最大值*/
51                 }
52             }
53         }
54     }    
55     
56     return ;
57 }
58 
59 int main(void)
60 {
61     init();
62     find();
63     printf("%lld",dp[n][k]);
64     return 0;
65 }

 

 

在過完思路后,可以發現由於會遍歷不同長度下的可能結果,而我們僅僅是要長度為n時的最大乘積,顯然程序做了大量的無用功

而到這里,思路也有所啟發

從題目中,我們可以得到的隱藏條件是n>1,且n>k

而題目的樣例輸入

4 2

1231

指兩個乘號,放在4個數據中間(左右兩端不能放),那么

第一個乘號的可能出現位置為:1*2*3*1 

第二個乘號的可能出現位置為:12*3*1 (至少已放了一個乘號)

 顯然,后面的乘號出現位置前面的乘號的位置有關系,可以通過標記擺放乘號,並用遞歸遍歷不同的乘號(dfs)

這樣,我們就可以得到一種情況下的數字分割

通過查乘號的出現位置,完成數字之間的相乘,並比較最大值,將最大值保存

 

改進代碼:

 1 #include <stdio.h> 
 2 #define MAX 46
 3 
 4 int n,k;
 5 long long num[MAX] = {0};/*每一位的數據存儲*/
 6 int vis[MAX] = {0};    /*每一位乘號的使用標記*/
 7 long long max = 0;        /*最大值存儲*/
 8 
 9 void init()
10 {
11     int i;
12     char tmp[MAX] = {0};
13     scanf("%d %d",&n,&k);
14     scanf("%s",&tmp);
15     for (i = 1 ; i <= n ; i ++)
16     {
17         num[i] = tmp[i-1]-'0';
18     }
19     return ;
20 }
21 
22 long long cut(int l,int r)
23 {
24     int i;
25     long long end = 0;
26     for (i = l ; i <= r ;i ++)
27     {
28         end = end*10 + num[i];        
29     }
30     return end;
31 }
32 
33 void dfs(int x) 
34 {
35     /*隱藏條件:N>1,N>K*/
36     int i;
37     int a,b;/*乘號分割下的數字左右下標*/
38     long long tmp;
39                 
40     if (x > k)/*乘號使用完畢*/
41     {
42         a = 0,b = 1;
43         tmp = 1;
44         /*計算該次搜索中的數字分割后的乘積*/
45         for (i = 1 ; i < n ; i ++)
46         {
47             if (vis[i])
48             {
49                 b = i;
50                 tmp *= cut(a+1,b);                
51                 a = b;
52             }            
53         }
54         tmp *= cut(a+1,n);
55         if (tmp > max)
56         {
57             max = tmp;
58         }
59         return ;
60     }
61      
62     for (i = x ; i < n ; i ++)/*遍歷不同位置的乘號*/
63     {
64         if (!vis[i])/*當前位置的乘號未使用*/
65         {
66             vis[i] = 1;
67             dfs(x+1);
68             vis[i] = 0;
69         }
70     }
71         
72     return ;
73 }
74 
75 int main(void)
76 {
77     init();
78     dfs(1);
79     printf("%lld",max);
80     return 0;
81 }

 


免責聲明!

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



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