1. 程式人生 > >題解 P1018 【乘積最大】

題解 P1018 【乘積最大】

題目連結:P1018 乘積最大

題面

今年是國際數學聯盟確定的“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設計一個程式,求得正確的答案。

題意

在一個長度為 N 的數字字串中加上 K 個乘號,使所得表示式值最大。

題解

前言: 洛谷資料加強了,用\(long long\)只有60分,我不講高精度乘法,只講如何解這道題。

  1. 由題中"可將它分成K+1個部分"得出,這是區間劃分類的題目。

  2. 要求找出區間最大值,因為乘號放在任何一個位置都不一定是等效的結果,有子結構。

  3. 要求最大值也就是間接性求最優子結構,子結構最優後的得出總體最優。符合無後效性。

題面題意分析後可以得出這是一道區間劃分DP題。

  1. 以加入的乘號數量作為劃分階段。

  2. \(f[n][k]\)

    表示原數字前 n 位中加入 k 個乘號所得表示式的最大值。

  3. 預處理出 \(a[i][j]\) 表示原數字第 i 位到第 j 位組成的數字。

  4. \(f[n][k] = max(f[i][k-1] * a[i+1][n],i∈[k,n))\)

程式碼(無高精度)

#include<bits/stdc++.h>
using namespace std;

long long dp[45][45],num[45][45];
bool tong[45][45];
int n,k;
char ch[45];

inline void init()
{
    cin >> n >> k;
    cin >> ch;

    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < n;j++)
        {
            if(i <= j)
            {
                for(int k = i;k <= j;k++)
                {
                    num[i][j]*=10;
                    num[i][j]+=ch[k]-'0';
                }
            }
        }
    }
}

long long dfs(int n,int k)
{
    if(k == 0) return num[0][n-1];
    if(tong[n-1][k-1]) return dp[n-1][k-1];

    for(int i = k;i < n;i++)
        dp[n-1][k-1] = max(dp[n-1][k-1],dfs(i,k-1)*num[i][n-1]);

    tong[n-1][k-1] = true;

    return dp[n-1][k-1];
}

int main(int argc, char const *argv[])
{
    init();
    cout << dfs(n,k);
    getchar();getchar();getchar();
    return 0;
}

有不懂的可以加我qq:2832853025