链接:https://ac.nowcoder.com/acm/problem/16757
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
今年是国际数学联盟确定的“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
输出
62
解题思路:
第一次见到这道题的时候想到的是dfs,但是dfs耗时太多,然后看答案有人说记忆化搜索,然后就是动态规划。
二维数组,其中第一维存到达第几个字符了(也就是位置index),第二维存用掉了几个乘号。BigInteger[][] dp = new BigInteger[N][K+1];
num二维数组用来存从 i 到 j 字符组成的数的值。
状态转移方程:dp[i][k] = max(dp[j][k-1] * num[j+1][i]);
也就是说,当我遍历到 i 这个位置,用了 k 个乘号,那么枚举前边的 【到 j 这个位置用 k-1 个乘号】* 【j+1 到 i 的值】的值,取最大的。
不过要注意的是位置太靠前的话,k 的值不能取太大,因为 假设位置是0,k就不能取1,2,3……,因为没有那么多位置能放乘号。同样 j 的取值也要注意与 k 的关系。
还有这个数可能会很大,所以要用 java 封装好的 BigInteger。
反思错误:
①没有注意到位置 i 与乘号个数 k 的关系。位置是 1 的时候,乘号个数绝对不能超过 1 个。
②还有就是位置 i 比所有乘号个数 K 还要大时,就不能再增加 k 了,最高只能到 K 。
③用大数BigInteger时,注意要初始化 new。还有关于 大数的运算 以及 比较的方式。
java代码(代码量大,但实际代码极少,在solution函数里):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
// long sta = System.nanoTime();
InputStream is = System.in;
OutputStream os = System.out;
IN cin = new IN(is);
PrintWriter cout = new PrintWriter(os);
SO so = new SO();
so.solution(cin, cout);
// long end = System.nanoTime();
// cout.println("耗时:" + (double)(end-sta)/1e6 + "ms");
cout.close();
}
static final int MOD = (int)1e9 + 7;
static class SO {
void solution(IN cin, PrintWriter cout) {
int N = cin.nextInt(), K = cin.nextInt();
BigInteger[][] num = new BigInteger[N][N];
BigInteger[][] dp = new BigInteger[N][K+1];
//初始化num数组
char[] cs = cin.next().toCharArray();
for(int i=0;i<N;++i) {
num[i][i] = BigInteger.valueOf(cs[i]-'0');
for(int j=i+1;j<N;++j) {
num[i][j] = num[i][j-1].multiply(BigInteger.TEN).add(BigInteger.valueOf(cs[j]-'0'));
}
}
//计算dp
for(int i=0;i<N;++i)
dp[i][0] = num[0][i];
for(int i=0;i<N;++i) {
for(int k=1;k<=(i<K?i:K);++k) {
dp[i][k] = BigInteger.ZERO;
for(int j=k-1;j<i;++j) {
BigInteger one = dp[j][k-1].multiply(num[j+1][i]);
dp[i][k] = one.compareTo(dp[i][k])>0?one:dp[i][k];
}
}
}
cout.println(dp[N-1][K]);
}//end solution
}//end SO
//以下是快读部分
static class IN {
private BufferedReader reader;
private StringTokenizer tokenizer;
IN(InputStream is) {
reader = new BufferedReader(new InputStreamReader(is), 32768);
tokenizer = null;
}
public String next() {
while (tokenizer == null || !tokenizer.hasMoreTokens()) {
try {
tokenizer = new StringTokenizer(reader.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return tokenizer.nextToken();
}
public int nextInt() {
return Integer.parseInt(next());
}
public long nextLong() {
return Long.parseLong(next());
}
public double nextDouble() {
return Double.parseDouble(next());
}
}
}
测试结果: