最大 k 乘积问题 ( 经典区间DP )
题意 : 设 NUM 是一个 n 位十进制整数。如果将 NUM 划分为 k 段,则可得到 k 个整数。这 k 个整数的乘积称为 NUM 的一个 k 乘积。试设计一个算法,对于给定的 NUM 和 k,求出 NUM 的最大 k 乘积
分析 :
定义 dp[i][j] = 前 i 个数字中间插入 j 个乘号时候的最大乘积是多少
初始化 dp[ i ][ 0 ] = NUM(1, i) 1 <= i <= len(NUM)
最后的结果则存于 dp[n][k]
状态转移方程为 dp[i][j] = max( dp[i][j] , dp[m][j-1] * NUM[m+1 ~ i] ) j-1 < m < i
注 : NUM[a~b] 表示 NUM 的第 a 位到第 b 位组成的数字
关于状态转移方程先来看一个例子 n = 4、k = 2、NUM = 1231
首先初始化
dp[1][0] = 1
dp[2][0] = 12
dp[3][0] = 123
dp[4][0] = 1231
然后安插 1 个乘号的时候各个长度的最大乘积
dp[2][1] = dp[1][0] * 2 = 2
dp[3][1] = max( dp[1][0]*23、dp[2][0]*3 ) = 36
dp[4][1] = max( dp[1][0]*231、dp[2][0]*31、dp[3][0]*1 ) = 372
接着是安插 2 个乘号的时候
dp[3][2] = dp[2][1] * 3 = 6
dp[4][2] = max( dp[2][1]*31、dp[3][1]*1 ) = 62
细细去推一下这个例子,可能就会发现更加理解了这个 dp
可以看出这个 dp 定义的第二维应该是阶段、而第一维是 dp 的状态
换句话说只有知道在各个长度安插 1 个乘号的结果才能推出各个长度安插 2 个乘号的结果
C++版
#include<bits/stdc++.h> #define LL long long using namespace std; const int maxn = 15; LL dp[maxn][maxn]; char num[maxn]; LL GetVal(int st, int en) { LL ret = 0; for(int i=st; i<=en; i++) ret = ret * 10 + (num[i] - '0'); return ret; } int main(void) { int n, k; while(~scanf("%d %d", &n, &k)){ scanf("%s", (num+1)); for(int i=1; i<=n; i++) dp[i][0] = GetVal(1, i); for(int j=1; j<=k-1; j++) for(int i=j+1; i<=n; i++) for(int m=j; m<i; m++) dp[i][j] = max(dp[i][j], dp[m][j-1]*GetVal(m+1, i)); printf("%lld\n", dp[n][k-1]); } return 0; }
JAVA大数版
import java.io.*; import java.lang.reflect.Array; import java.util.*; import java.math.*; import java.util.Arrays; public class Main { static BigInteger[][] dp = new BigInteger[15][15]; public static void main(String[] args){ Scanner cin = new Scanner (new BufferedInputStream(System.in)); int n, k; String s; while(cin.hasNext()){ n = cin.nextInt(); k = cin.nextInt(); s = cin.next(); for(int i=1; i<=n; i++){ dp[i][0] = BigInteger.valueOf( Integer.parseInt(s.substring(0,i)) ); //System.out.println(dp[i][0]); } for(int j=1; j<=k-1; j++) for(int i=j+1; i<=n; i++){ dp[i][j] = BigInteger.ZERO; for(int m=j; m<i; m++){ if(dp[i][j].compareTo(dp[m][j-1].multiply( BigInteger.valueOf( Integer.parseInt(s.substring(m, i)) ) )) < 0) dp[i][j] = dp[m][j-1].multiply( BigInteger.valueOf( Integer.parseInt(s.substring(m, i)) ) ); } } System.out.println(dp[n][k-1]); } } }