最佳加法表达式
- 总时间限制: 1000ms 内存限制: 65536kB
- 描述
-
给定n个1到9的数字,要求在数字之间摆放m个加号(加号两边必须有数字),使得所得到的加法表达式的值最小,并输出该值。例如,在1234中摆放1个加号,最好的摆法就是12+34,和为36
- 输入
- 有不超过15组数据
每组数据两行。第一行是整数m,表示有m个加号要放( 0<=m<=50)
第二行是若干个数字。数字总数n不超过50,且 m <= n-1 - 输出
- 对每组数据,输出最小加法表达式的值
- 样例输入
-
2 123456 1 123456 4 12345
- 样例输出
-
102 579 15
- 提示
- 要用到高精度计算,即用数组来存放long long 都装不下的大整数,并用模拟列竖式的办法进行大整数的加法。
算法参考:http://www.cnblogs.com/quintessence/p/7206417.html 使用了高精度
http://www.cnblogs.com/Renyi-Fan/p/6970166.html 没有使用高精度,有详细分析
假定数字串长度是n,添完加号后,表达式的最后一个加号添加在第 i 个数字后面,
那么整个表达式的最小值,就等于在前 i 个数字中插入 m – 1个加号所能形成的最小值,
加上第 i + 1到第 n 个数字所组成的数的值(i从1开始算)。
设V(m,n)表示在n个数字中插入m个加号所能形成
的表达式最小值,那么:
if m = 0
V(m,n) = n个数字构成的整数
else if n < m + 1
V(m,n) = ∞
else
V(m,n) = Min{ V(m-1,i) + Num(i+1,n) } ( i = m … n-1)
Num(i,j)表示从第i个数字到第j个数字所组成的数。数字编号从1开始算。此操作复杂度是O(j-i+1)
总时间复杂度:O(mn2) .(dp二维表已经加号的位置)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int INF=0x3f3f3f3f; 7 const int N=1005; 8 int a[N],num[N][N],dp[N][N]; 9 //a[N]里面是存数字串 10 //num[i][j]表示数字串a[N]的第i位到第j位之间的数字串表示的数组 11 //dp[i][j]在i个数字中插入j个加号所能形成的表达式最小值 12 int main(){ 13 int n,m; 14 while(scanf("%d %d",&n,&m)){ 15 for(int i=1;i<=n;i++){ 16 scanf("%d",&a[i]); 17 } 18 //预处理,计算i到j数字串组成的数字 19 for(int i=1;i<=n;i++){ 20 num[i][i]=a[i];//只有一个数字 21 for(int j=i+1;j<=n;j++){ 22 num[i][j]=num[i][j-1]*10+a[j]; 23 } 24 } 25 memset(dp,0x3f,sizeof(dp)); 26 for(int i=1;i<=n;i++){ 27 dp[0][i]=num[1][i];//无加号时 28 } 29 //其实就是感觉在那个位置放不放加号 30 //这里n可以写在m前面。要加一个限制条件n>m,好麻烦,所以m在前且n=m+1 31 //这里k的取值范围就是m到n,k表示在第k个数后面插入加号 32 for(int i=1;i<=m;i++) 33 for(int j=i;j<=n;j++) 34 for(int k=i;k<=j;k++) 35 dp[i][j]=min(dp[i][j],dp[i-1][k]+num[k+1][j]); 36 cout<<dp[m][n]<<endl; 37 } 38 }