乘积最大
好不容易写出来,int改long long又改高精才改对的。
先上题目——
洛谷P1018 [NOIP2000 提高组] 乘积最大
题目描述
今年是国际数学联盟确定的“ 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)
第二行是一个长度为 NN 的数字串。
输出格式
结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。
输入输出样例
4 2 1231
62
说明/提示
NOIp2000提高组第二题
谈谈思路——
既然说是分成k+1个部分相乘,那么我就开一个数组存着k+1个部分,这样就是一个二维dp,第几个数在第几个部分,判断两种情况,在这个数后放乘号,下个数进入下个部分,或不在这个数后放乘号,下个数依然在这个部分,int和long long都很简单,主要是高精比较难实现。
我用的方法是用void定义dp函数,其中有一个参数是数组,通过递归对数组进行操作实现高精。
#include<iostream> #include<cstring> using namespace std; int n,k; string s; int a[200]; long long f[200][10]; bool bj(int x[200],int y[200]){ if(x[0]>y[0])return true; if(x[0]==y[0]){ for(int j=x[0];j>=1;j--){ if(x[j]>y[j])return true; if(x[j]<y[j])return false; } } return false; } void dp(long long qj[10][200],int ans[200],int i,int cnt){ if(i==n&&cnt<k+1){ ans[0]=1; ans[1]=0; return ; } for(int j=qj[cnt][0];j>=1;j--){ qj[cnt][j+1]=qj[cnt][j]; } qj[cnt][0]++; qj[cnt][1]=a[i]; if(i==n){ for(int j=0;j<=qj[cnt][0];j++){ ans[j]=qj[cnt][j]; } } else{ dp(qj,ans,i+1,cnt); if(cnt<k+1){ int ans1[200],ans2[200]; for(int j=0;j<=199;j++){ ans1[j]=0; ans2[j]=0; } dp(qj,ans1,i+1,cnt+1); for(int j=1;j<=ans1[0];j++){ for(int l=1;l<=qj[cnt][0];l++){ ans2[j+l-1]+=ans1[j]*qj[cnt][l]; } } ans2[0]=ans1[0]+qj[cnt][0]-1; for(int j=1;j<=ans2[0];j++){ ans2[j+1]+=ans2[j]/10; ans2[j]%=10; } if(ans2[ans2[0]+1]>0){ ans2[0]++; } while(ans2[ans2[0]]==0){ ans2[0]--; } if(bj(ans2,ans)){ for(int j=0;j<=ans2[0];j++){ ans[j]=ans2[j]; } } } } for(int j=2;j<=qj[cnt][0];j++){ qj[cnt][j-1]=qj[cnt][j]; } qj[cnt][qj[cnt][0]]=0; qj[cnt][0]--; return ; } int main(){ cin>>n>>k; cin>>s; for(int i=1;i<=n;i++){ a[i]=s[i-1]-'0'; } long long qj[10][200]; for(int i=1;i<=7;i++){ for(int j=1;j<=199;j++){ qj[i][j]=0; } } int ans[200]; dp(qj,ans,1,1); for(int i=ans[0];i>=1;i--){ cout<<ans[i]; } return 0; }
感谢一位大佬,我从他那里学到一种方法,比我这个好,就是定义一个结构体node,用node定义dp函数,使得dp函数的返回值是类似于数组的,以此实现高精。
#include<iostream> #include<cstring> using namespace std; int n,k; string s; int a[50]; struct node{ int gao[200]; }; node f[50][10]; bool bj(node x,node y){ if(x.gao[0]>y.gao[0])return true; if(x.gao[0]==y.gao[0]){ for(int j=x.gao[0];j>=1;j--){ if(x.gao[j]>y.gao[j])return true; if(x.gao[j]<y.gao[j])return false; } } return false; } node dp(node qj[10],int i,int cnt){ if(i==n&&cnt!=k+1){ f[i][cnt].gao[0]=1; f[i][cnt].gao[1]=0; return f[i][cnt]; } for(int j=qj[cnt].gao[0];j>=1;j--){ qj[cnt].gao[j+1]=qj[cnt].gao[j]; } qj[cnt].gao[1]=a[i]; qj[cnt].gao[0]++; if(i==n) f[i][cnt]=qj[cnt]; else{ f[i][cnt]=dp(qj,i+1,cnt); if(cnt<k+1){ node t=dp(qj,i+1,cnt+1); node ans; for(int j=0;j<=199;j++){ ans.gao[j]=0; } for(int j=1;j<=t.gao[0];j++){ for(int l=1;l<=qj[cnt].gao[0];l++){ ans.gao[j+l-1]+=t.gao[j]*qj[cnt].gao[l]; } } ans.gao[0]=t.gao[0]+qj[cnt].gao[0]-1; for(int j=1;j<=ans.gao[0];j++){ ans.gao[j+1]+=ans.gao[j]/10; ans.gao[j]%=10; } if(ans.gao[ans.gao[0]+1]>0){ ans.gao[0]++; } while(ans.gao[ans.gao[0]]==0){ ans.gao[0]--; } if(bj(ans,f[i][cnt])){ f[i][cnt]=ans; } } } for(int j=2;j<=qj[cnt].gao[0];j++){ qj[cnt].gao[j-1]=qj[cnt].gao[j]; } qj[cnt].gao[qj[cnt].gao[0]]=0; qj[cnt].gao[0]--; return f[i][cnt]; } int main(){ cin>>n>>k; cin>>s; for(int i=1;i<=n;i++){ a[i]=s[i-1]-'0'; } node qj[10]; for(int i=1;i<=7;i++){ for(int j=0;j<=199;j++){ qj[i].gao[j]=0; } } node t=dp(qj,1,1); for(int i=t.gao[0];i>=1;i--){ cout<<t.gao[i]; } return 0; }
因为代码里的高精使得DP的思路看的不是那么清楚,所以我就发一下非高精代码:
#include<iostream> #include<cstring> using namespace std; int n,k; string s; int a[50]; long long f[50][10]; long long dp(long long qj[10],int i,int cnt){ if(i==n&&cnt!=k+1) return 0; qj[cnt]*=10; qj[cnt]+=a[i]; if(i==n) f[i][cnt]=qj[cnt]; else if(cnt==k+1) f[i][cnt]=dp(qj,i+1,cnt); else f[i][cnt]=max(dp(qj,i+1,cnt),qj[cnt]*dp(qj,i+1,cnt+1)); qj[cnt]/=10; return f[i][cnt]; } int main(){ cin>>n>>k; cin>>s; for(int i=1;i<=n;i++){ a[i]=s[i-1]-'0'; } long long qj[10]; for(int i=1;i<=7;i++){ qj[i]=0; } cout<<dp(qj,1,1); return 0; }
2022.3.18
今天又想到一个新思路:
二维数组f[i,j]表示数字串1—i的部分中有j个乘号,且保证i和i+1间是一个乘号,那么我们就需要枚举前一个乘号的位置,找到f[k][i-1]*(k+1到i这段数字串)的最小值。
下面就放一下非高精的代码:
#include<iostream> using namespace std; int n,k; int a[50]; void read(){ char b=getchar(); while(b<'0'||b>'9'){ b=getchar(); } int cnt=1; a[cnt]=b-'0'; while(cnt<=n){ b=getchar(); a[++cnt]=b-'0'; } } int f[50][10]; int wucheng(int i,int j){ int cnt=0; for(int l=i;l<=j;l++){ cnt*=10; cnt+=a[l]; } return cnt; } int main(){ cin>>n>>k; read(); for(int i=1;i<=n;i++){ f[i][0]=f[i-1][0]*10+a[i]; } for(int i=2;i<=n;i++){ for(int j=1;j<=i-1&&j<=k;j++){ for(int l=j;l<i;l++){ f[i][j]=max(f[i][j],f[l][j-1]*wucheng(l+1,i)); } } } cout<<f[n][k]; return 0; }