p1018 乘积最大
题目描述
今年是国际数学联盟确定的“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)
第二行是一个长度为N的数字串。
输出格式
结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。
输入输出样例
输入
4 2
1231
输出
62
题解
在n个数中插入k个乘号.
首先看到位数范围40位, 就能想到, 乘法操作后, 可能会爆long long, 保险起见, 上高精.
对于这个题的设定, 第i个乘号确定后将数分成的两部分, 乘号数量一定(左i-1个和右k-i个), 乘积互不影响. 所以只要保证两边单独的乘积最大, 就能保证最后的乘积最大.
这样就设计出了状态:
f(i,j)表示第j个乘号放在第i个数字之后, 前i个数字的乘积最大值.
设A[l,r]表示用A的第l位到第r位形成的高精数
那么就可以写出状态转移方程:
[f[i][j]=max(f[j-1][j-1]*A[j,i],f[j][j-1]*A[j+1,i],...,f[i-1][j-1]*A[i,i])
]
最后答案也可以通过f数组推出.
[ans=max(f[k][k]*A[k+1,n],...,f[n-1][k]*A[n,n])
]
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n, K, A[50], C[1005], b[50][10][1005], d[1005], ji[1005], ans[1005];
char a[50];
bool flg[1005];
void take(int l, int r) {//从A中取[l,r]区间的数字组成一个数, 存入高精数d.
d[0] = r - l + 1;
for (int ti = l; ti <= r; ti++) {
d[ti - l + 1] = A[ti];
}
return;
}
void print(int x[]) {//打印高精数x
for (int pi = x[0]; pi >= 1; pi--) {
cout << x[pi];
}
cout << endl;
return;
}
void times(int x[], int y[]) {//计算高精数x,y的乘积, 并且将积存入高精数ji
memset(ji, 0, sizeof(ji));
int xx = 0;
for (int ti = 1; ti <= x[0]; ti++)//枚举因数A的每一位
{
xx = 0;
for (int tj = 1; tj <= y[0]; tj++) {//枚举因数B的每一位
ji[ti + tj - 1] += xx + (x[ti] * y[tj]);
xx = ji[ti + tj - 1] / 10;
ji[ti + tj - 1] %= 10;
}
ji[ti + y[0]] += xx;
}
ji[0] = x[0] + y[0];
while (ji[ji[0]] == 0 && ji[0] > 1) {
ji[0]--;
}
return;
}
bool cmp(int x[],int y[]) {//比较高精数x,y, 若x>y则返回true, 反之返回false
if (x[0] < y[0]) {
return false;
}
if (x[0] > y[0]) {
return true;
}
for (int ci = x[0]; ci >= 1; ci--) {
if (x[ci] < y[ci]) {
return false;
}
if (x[ci] > y[ci]) {
return true;
}
}
return false;
}
int main() {
cin >> n >> K;
for (int i = 1; i <= n; i++) {
cin >> a[i];
A[n - i + 1] = a[i] - 48;//倒着存, 高精常规操作, 不解释
}
A[0] = n;//高精数第0位存长度
for (int i = 1; i < n; i++) {//枚举
take(1, i);//这是取b[i][1]的值
for (int ij = 0; ij <= d[0]; ij++) {//只放一个乘号, 结果固定
b[i][1][ij] = d[ij];
}
for (int j = 2; j <= K; j++) {//枚举乘号
for (int k = j; k < i; k++) {//枚举区间右界
take(k + 1, i);//取A数的第k+1位到第i位作为一个乘数
//print(b[k][j - 1]);//注释语句均为调试所用, 保留原因见最后一行
times(b[k][j - 1], d);//代入第二个乘数,计算乘法
//print(b[k][j - 1]);
//cout << i << " " << j << endl;
if (cmp(ji, b[i][j])) {//比较并更新(转移)
for (int ci = 0; ci <= ji[0]; ci++) {
b[i][j][ci] = ji[ci];
}
}
}
}
}
for (int i = K; i <= n - 1; i++) {//枚举放完所有的乘号后, 所得的答案
take(i + 1, n);
times(b[i][K], d);//乘法
//cout << i << " " << K<< endl;
//print(d);
//print(b[i][K]);
//print(ji);
if (cmp(ji, ans)) {//比较并更新答案
for (int j = ji[0]; j >= 0; j--) {
ans[j] = ji[j];
}
}
}
print(ans);//输出答案
return 0;
}//保留调试语句刚好100行