最佳加法表達式
- 總時間限制: 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 }