最佳加法表達式


最佳加法表達式

總時間限制: 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 }
未使用高精度的代碼

 

下面是使用了高精度數據結構的算法代碼:

首先,由於 數據范圍顯然需要用到高精度,高精度的寫法不再贅述。

dp[i][j]表示前i個數添加了j個加號得到的最小和。轉移方程為 dp[i][j]=min(dp[x][j-1]+num[x+1][i]) 其中x>=j且x<i num[x+1][i]表示第x+1位到第i位組成的數。

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <queue>
 8 #include <set>
 9 #include <map>
10 #include <list>
11 #include <vector>
12 #include <stack>
13 #define mp make_pair
14 //#define P make_pair
15 #define MIN(a,b) (a>b?b:a)
16 //#define MAX(a,b) (a>b?a:b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int MAX=1e2+5;
20 const int INF=1e8+5;
21 using namespace std;
22 //const int MOD=1e9+7;
23 typedef pair<ll,int> pii;
24 const double eps=0.00000001;
25 
26 string add(string x,string y)
27 {
28     string re;
29     int jin=0;
30     for(int i=x.length()-1,j=y.length()-1;i>=0||j>=0;i--,j--)
31     {
32         re=" "+re;
33         re[0]=(i>=0?x[i]-'0':0)+(j>=0?y[j]-'0':0)+jin;
34         if(re[0]>=10)
35             jin=1,re[0]=(re[0]%10)+'0';
36         else
37             jin=0,re[0]=re[0]+'0';
38     }
39     if(jin)
40         re='1'+re;
41     return re;
42 }
43 string mins(string x,string y)
44 {
45     if(x.length()<y.length())
46         return x;
47     else if(y.length()<x.length())
48         return y;
49     else return x<y?x:y;
50 }
51 int m;
52 string x;
53 string dp[55][55];
54 int main()
55 {
56     while(~scanf("%d",&m))
57     {
58         cin>>x;
59         int len=x.length();
60         x=" "+x;
61         for(int i=0;i<=len;i++)
62             dp[i][0]=x.substr(1,i);
63         for(int j=1;j<=m;j++)
64             for(int i=j+1;i<=len;i++)
65                 for(int s=j;s<i;s++)
66                 {
67                     if(s==j)
68                         dp[i][j]=add(dp[s][j-1],x.substr(s+1,i-s));
69                     else
70                         dp[i][j]=mins(dp[i][j],add(dp[s][j-1],x.substr(s+1,i-s)));
71                 }
72         cout<<dp[len][m]<<"\n";
73     }
74 }

 

下面是自己寫的原始高精度算法的代碼,結果超時了。。。

  1 #include <stdio.h>
  2 #include<string.h>
  3 
  4 #define MaxLength 91
  5 int m,len,a[MaxLength];
  6 long long count=0;
  7 
  8 void init(char str[],int a[]);
  9 void add(int a[],int b[],int c[]);
 10 int cmp(int a[],int b[]);
 11 void fun(int m,int n,int min[]);
 12 void num(int i,int j,int t[]);
 13 void output(int minSum[]);
 14 
 15 int main(int argc, char *argv[])
 16 {
 17     freopen("003.txt","r",stdin);
 18     freopen("003.out","w",stdout);
 19     char str[MaxLength];
 20     int minSum[MaxLength];
 21     int i;
 22     
 23     while(scanf("%d%s",&m,str)!=EOF)
 24     {
 25         init(str,a);
 26         len=a[0];
 27         /*printf("%d\n",m);
 28         for(i=1;i<=a[0];i++) printf("%d",a[i]);
 29         printf("\n");*/
 30         for(i=1;i<MaxLength;i++) { minSum[i]=9; }
 31         minSum[0]=MaxLength+4;
 32         
 33         fun(m,len,minSum);
 34         output(minSum);
 35     }
 36     
 37     /*char str1[20]="1",str2[20]="234";
 38     int aa[25],bb[25],cc[25];
 39     
 40     init(str1,aa);
 41     init(str2,bb);
 42     for(int i=0;i<=aa[0];i++) printf("%d",aa[i]);printf("\n");
 43     for(int i=0;i<=bb[0];i++) printf("%d",bb[i]);printf("\n");
 44     add(aa,bb,cc);
 45     for(int i=0;i<=cc[0];i++) printf("%d",cc[i]);printf("\n");*/
 46     
 47     return 0;
 48 }
 49 
 50 /******************************************************************************/
 51 /*輸入一個字符串str表示高精度正整數
 52   將str轉換成int數組存儲在a[]
 53   a[0]存儲str代表的數字的位數
 54   str正序存儲在a[]中 
 55 */
 56 void init(char str[],int a[])
 57 {
 58     int i;
 59     //memset(a,0,sizeof(int)*MaxLength);
 60     a[0]=strlen(str);
 61     for(i=1;i<=a[0];i++)
 62         a[i]=str[i-1]-'0';
 63 }
 64 /******************************************************************************/
 65 void fun(int m,int n,int min[])
 66 {
 67     int i,j,x[MaxLength]={0},y[MaxLength]={0},sum[MaxLength];
 68     //printf("m=%d n=%d\n",m,n);
 69     if(m==0)
 70     {
 71         num(1,n,min);
 72     }
 73     else if(n<m+1)
 74     {
 75         for(i=1;i<MaxLength;i++) { min[i]=9;}
 76         min[0]=MaxLength+2;
 77     }
 78     else 
 79     {
 80         for(i=0;i<MaxLength;i++) min[i]=9;
 81         min[0]=MaxLength+2;
 82         
 83         for(i=m;i<n;i++)
 84         {
 85             fun(m-1,i,x);
 86             num(i+1,n,y);
 87             add(x,y,sum);
 88             if(cmp(sum,min)==-1)
 89             {    for(j=0;j<=sum[0];j++) min[j]=sum[j];    }
 90         }
 91     }
 92 }
 93 /******************************************************************************/
 94 void num(int i,int j,int t[])
 95 {
 96     count++;
 97     //printf("%d\n",count);
 98     
 99     int k;
100     t[0]=j-i+1;
101     for(k=1;i<=j;k++,i++) t[k]=a[i];
102 }
103 /******************************************************************************/
104 /*  輸入高精度正整數a和b,計算c=a+b   */
105 void add(int a[],int b[],int c[])
106 {
107     int x=0,i=a[0],j=b[0],k=1;
108     //memset(c,0,sizeof(int)*MaxLength);
109     //這個地方初始化的字節數的解釋:c[]從主調函數傳過來,c的元素個數未知。
110     //sizeof(c)只能測出一個元素的字節數,而不是整個數組的字節數 
111     
112     while(i>0&&j>0)
113     {
114         c[k]=a[i]+b[j]+x;//x表示加法的進位 
115         x=c[k]/10;    //新產生的進位 
116         c[k]=c[k]%10; //只保留個位 
117         k++;
118         i--;
119         j--;
120     }
121     while(i>0)
122     {
123         c[k]=a[i]+x;
124         x=c[k]/10;
125         c[k]=c[k]%10;
126         k++;
127         i--;
128     }
129     while(j>0)
130     {
131         c[k]=b[j]+x;
132         x=c[k]/10;
133         c[k]=c[k]%10;
134         k++;
135         j--;
136     }
137     c[k]=x;  //可能有向更高位的進位 
138     if(c[k]==0) k--;
139     c[0]=k;
140     
141     for(i=1,j=c[0];i<j;i++,j--)
142     {
143         x=c[i]; c[i]=c[j]; c[j]=x;
144     }
145 }
146 /******************************************************************************/
147 /*  比較a[]和b[]表示的兩個高精度整數的大小,返回1表示a>b,0表示相等,-1表示a<b   */
148 int cmp(int a[],int b[])
149 {
150     int i;
151     if(a[0]<b[0]) return -1;
152     else if(a[0]>b[0]) return 1;
153     else
154     {
155         i=1;
156         while(i<=a[0]&&a[i]==b[i]) i++;
157         if(i>a[0]) return 0;
158         else if(a[i]<b[i])return -1;
159         else return 1;
160     }
161 }
162 /******************************************************************************/
163 void output(int minSum[])
164 {
165     int i;
166     for(i=1;i<=minSum[0];i++)
167     {
168         printf("%d",minSum[i]);
169     }
170     printf("\n");
171 }
View Code

 


免責聲明!

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



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