1. 程式人生 > >動規之最大k乘積問題

動規之最大k乘積問題

最大k乘積問題

問題描述
設I是一個n位十進位制整數。如果將I分割為k段,則可得到k個整數。這k個整數的乘積稱為I的一個k乘積。試設計一個演算法,對於給定的I和k,求出I的最大k乘積。

樣例輸入:

  • 第一組
    5 2
    12345

  • 第二組
    5 3
    4 5 6 2 1

樣例輸出

  • 第一組
    6170

  • 第二組
    12420

問題分析:

典型的區間動規問題

dp[i][j] 的意思就是前i位數,分成j段時的最優解

最優子結構:

dp[i][j] = dp[k][j-1] * m[k+1][i];

前i位數,分成j段時的最優解,可以看成前k位,分成j-1段時的最優解乘上最後i - k 位的值(m[k+1][j])的值。dp[k][j-1]就是這個問題的子問題。而且他又可以寫成他的子問題乘的形式。
從上面的解釋我們可以看出存在大量的重複子問題,但是由於演算法是自底向上的,之前計算過的最優值已經被儲存的數組裡,當問題呼叫他的子問題的時候,只需取出陣列中的數即可,免去了重複計算所帶來的時間複雜度,這就是動規的精髓所在。

程式碼

/*
最大k乘積問題 
 
設I是一個n位十進位制整數。如果將I分割為k段,則可得到k個整數。這k個整數的乘積稱為I的一個k乘積。
試設計一個演算法,對於給定的I和k,求出I的最大k乘積。

*/
#include <cstdio> #include <cstring> #include <iostream> #include<cstdio> using namespace std; #define MAX_N 110 static int dp[MAX_N][MAX_N];//從i開始往後j位數 static int m[MAX_N][MAX_N];//m數組裡儲存的是從第i位到第j位的數是多少 void dp1(int n) { for(int i=1;i<=n;i++)//初始化,前i位數字分一段就是第一位到第 i位表示的數
{ dp[i][1] = m[1][i]; } for(int j=2;j<=n;j++)//分割段數 { for(int i=j;i<=n;i++)//位數 { for(int k=1;k<i;k++) { dp[i][j]=max(dp[i][j],dp[k][j-1]*m[k+1][i]); } } } } int main() { int n,k; cout << "請輸入數字的位數 ,要分成的段數" << endl; while(cin >> n >> k) { memset(m,0,MAX_N*MAX_N); memset(dp,0,MAX_N*MAX_N); cout << "請輸入一個" << n << "位數" << endl; for(int i=1;i<=n;i++) { char ch; cin>>ch; m[i][i]=ch-'0'; } for(int i=1;i<=n;i++) { for(int j=i + 1;j<=n;j++) { m[i][j] = m[i][j-1]*10 + m[j][j]; } } dp1(n); printf("%d位數分成%d段相乘所得到的最大值是: %d\n",n,k,dp[n][k]); // cout << "最優值表" << endl; // for(int i = 1;i <=n;i ++ ){ // for(int j = 1;j <= i;j ++){ // cout << dp[i][j] << " "; // } // cout << endl; // } cout << endl << "請輸入n ,k" << endl; } return 0; }