問題描述 今年是國際數學聯盟確定的“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≤40,1≤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 }