動規之最大k乘積問題
阿新 • • 發佈:2018-12-09
最大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;
}