题目描述
今年是国际数学联盟确定的“2000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
1、3×12=36 2、31×2=62
这时,符合题目要求的结果是: 31×2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
输入输出格式
输入格式:
程序的输入共有两行:
第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
第二行是一个长度为N的数字串。
输出格式:
结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。
输入输出样例
输入样例#1: 复制
4 2
1231
输出样例#1: 复制
62
预处理中可以先把字符串中第i至j位的数都算出来,用num[i][j]表示。
用dp[i][j]表示前i个数插入j个乘号所得的最大乘积,递推关系时,dp[i[[j]与dp[k][j-1]有关联,其中k的范围是[j,i-1],表示最后一个乘号的位置(第k个数与第k+1个数的间隔)。
注意精度,用__int128处理的时候精度还是不够,此处可参考上一篇博客介绍的方法C++高精度模板
80分代码:
#include<iostream>
#include<sstream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
using namespace std;
__int128 dp[42][7];//dp[i][j]表示前i个数插入j个乘号的最大乘积 (从1开始)
__int128 num[42][42];//num[i][j]表示字符串中第i位至第j为组成的数(从1开始)
inline void output(__int128 a)
{
if(a>9) output(a/10);
putchar(a%10+'0');
}
int main()
{
int N,K;
string str;
cin>>N>>K>>str;
memset(dp,0,sizeof(dp));
memset(num,0,sizeof(num));
for(int i=1;i<=N;i++)
{
for(int j=i;j<=N;j++)
num[i][j]=num[i][j-1]*10+str[j-1]-'0';
}
for(int i=1;i<=N;i++) dp[i][0]=num[1][i];
for(int j=1;j<=K;j++)//乘号个数
for(int i=2;i<=N;i++)//前i个数
for(int k=j;k<=i-1;k++)//最后一个乘号放在第k个数后第k+1个数前
dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i]);
output(dp[N][K]);
}
AC代码:
#include<iostream>
#include<sstream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
using namespace std;
struct Wint:vector<int>//用标准库vector做基类,完美解决位数问题,同时更易于实现
{
//将低精度转高精度的初始化,可以自动被编译器调用
//因此无需单独写高精度数和低精度数的运算函数,十分方便
Wint(int n=0)//默认初始化为0,但0的保存形式为空
{
push_back(n);
check();
}
Wint& check()//在各类运算中经常用到的进位小函数,不妨内置
{
while(!empty()&&!back())pop_back();//去除最高位可能存在的0
if(empty())return *this;
for(int i=1; i<size(); ++i)
{
(*this)[i]+=(*this)[i-1]/10;
(*this)[i-1]%=10;
}
while(back()>=10)
{
push_back(back()/10);
(*this)[size()-2]%=10;
}
return *this;//为使用方便,将进位后的自身返回引用
}
};
//输入输出
istream& operator>>(istream &is,Wint &n)
{
string s;
is>>s;
n.clear();
for(int i=s.size()-1; i>=0; --i)n.push_back(s[i]-'0');
return is;
}
ostream& operator<<(ostream &os,const Wint &n)
{
if(n.empty())os<<0;
for(int i=n.size()-1; i>=0; --i)os<<n[i];
return os;
}
//比较,只需要写两个,其他的直接代入即可
//常量引用当参数,避免拷贝更高效
bool operator!=(const Wint &a,const Wint &b)
{
if(a.size()!=b.size())return 1;
for(int i=a.size()-1; i>=0; --i)
if(a[i]!=b[i])return 1;
return 0;
}
bool operator==(const Wint &a,const Wint &b)
{
return !(a!=b);
}
bool operator<(const Wint &a,const Wint &b)
{
if(a.size()!=b.size())return a.size()<b.size();
for(int i=a.size()-1; i>=0; --i)
if(a[i]!=b[i])return a[i]<b[i];
return 0;
}
bool operator>(const Wint &a,const Wint &b)
{
return b<a;
}
bool operator<=(const Wint &a,const Wint &b)
{
return !(a>b);
}
bool operator>=(const Wint &a,const Wint &b)
{
return !(a<b);
}
//加法,先实现+=,这样更简洁高效
Wint& operator+=(Wint &a,const Wint &b)
{
if(a.size()<b.size())a.resize(b.size());
for(int i=0; i!=b.size(); ++i)a[i]+=b[i];
return a.check();
}
Wint operator+(Wint a,const Wint &b)
{
return a+=b;
}
//减法,返回差的绝对值,由于后面有交换,故参数不用引用
Wint& operator-=(Wint &a,Wint b)
{
if(a<b)swap(a,b);
for(int i=0; i!=b.size(); a[i]-=b[i],++i)
if(a[i]<b[i])//需要借位
{
int j=i+1;
while(!a[j])++j;
while(j>i)
{
--a[j];
a[--j]+=10;
}
}
return a.check();
}
Wint operator-(Wint a,const Wint &b)
{
return a-=b;
}
//乘法不能先实现*=,原因自己想
Wint operator*(const Wint &a,const Wint &b)
{
Wint n;
n.assign(a.size()+b.size()-1,0);
for(int i=0; i!=a.size(); ++i)
for(int j=0; j!=b.size(); ++j)
n[i+j]+=a[i]*b[j];
return n.check();
}
Wint& operator*=(Wint &a,const Wint &b)
{
return a=a*b;
}
Wint dp[42][7];//dp[i][j]表示前i个数插入j个乘号的最大乘积 (从1开始)
Wint num[42][42];//num[i][j]表示字符串中第i位至第j为组成的数(从1开始)
int main()
{
int N,K;
string str;
cin>>N>>K>>str;
memset(dp,0,sizeof(dp));
memset(num,0,sizeof(num));
for(int i=1;i<=N;i++)
{
for(int j=i;j<=N;j++)
num[i][j]=num[i][j-1]*10+str[j-1]-'0';
}
for(int i=1;i<=N;i++) dp[i][0]=num[1][i];
for(int j=1;j<=K;j++)//乘号个数
for(int i=2;i<=N;i++)//前i个数
for(int k=j;k<=i-1;k++)//最后一个乘号放在第k个数后第k+1个数前
dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i]);
cout<<dp[N][K];
}