最大 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;
}
View Code

 

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]);
        }
    }
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM