问题描述 今年是国际数学联盟确定的“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 }