[牛客][NOIP2000提高组]乘积最大(Java)(动态规划+高精度)

原题链接:乘积最大

链接: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());
		}

	}
}

 

测试结果:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值