[NOIp2000 T2] 乘積最大(序列dp)

題目

描述

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

輸入

程序的輸入共有兩行:
第一行共有2個自然數N,K(6≤N≤40,1≤K≤6)
第二行是一個長度爲N的數字串。

輸出

結果顯示在屏幕上,相對於輸入,應輸出所求得的最大乘積(一個自然數)。

輸入樣例

4  2
1231

輸出樣例

62

解題思路

直接搜索?複雜度O(CNK) ,記憶化之後貌似還可以過。
不過這是一道經典的dp問題(畢竟記憶化搜索和dp本質是一樣的):

  • dp狀態:dp[i][j] 表示前i 位數字中插入j 個乘號能得到的最大值
  • dp方程:dp[i][j]=max{ dp[k][j1]Ak+1...i  |  jk<i } ,其中Ai...j 表示原數字中第i 位到第j 位組成的數字
  • dp順序:從dp方程易知,i,j,k 從小到大依次循環即可,一定要注意範圍
  • 邊界條件:dp[i][0]=A1...i ,即前i 位一個乘號也不加就是它本身
    答案就是dp[N][K]

注意,這道題需要用高精度!

代碼技巧

  1. 我是用的string存儲,從string裏面提取一段連續子序列可以用s.substr(pos, len)提取出從pos開始的長度爲len的一段string類型字符串
  2. 建議寫高精度的題時用重載運算符的方式,這樣可以先寫一份無高精度的代碼檢驗算法正確性,過了樣例後改成高精度時十分方便

Code

#include<cstdio>
#include<vector>
#include<string>
#include<iostream>

using namespace std;

typedef long long LL;

struct BigInteger{
    vector<int> a;
    BigInteger(){ a.clear(); }
    BigInteger operator = (LL x){
        a.clear();
        do{
            a.push_back(x % 10);
            x /= 10;
        }while(x);
        return *this;
    }
    BigInteger operator = (const string &s){
        a.clear();
        for(int i = s.size() - 1; i >= 0; i--)
            a.push_back(s[i] - '0');
        return *this;
    }
    BigInteger operator + (const BigInteger &B) const{
        BigInteger C;
        for(int i = 0, g = 0; ; i++){
            if(i >= a.size() && i >= B.a.size() && !g)  break;
            int x = g;
            if(i < a.size())    x += a[i];
            if(i < B.a.size())  x += B.a[i];
            C.a.push_back(x % 10);
            g = x / 10;
        }
        return C;
    }
    BigInteger operator * (const BigInteger &B) const{
        BigInteger C;
        C.a.resize(a.size() + B.a.size());
        for(int i = 0; i < a.size(); i++){
            int g = 0;
            for(int j = 0; j < B.a.size(); j++){
                C.a[i+j] += g + a[i] * B.a[j];
                g = C.a[i+j] / 10;
                C.a[i+j] %= 10;
            }
            C.a[i+B.a.size()] = g;
        }
        while(C.a.size() > 1 && C.a.back() == 0)    C.a.pop_back();
        return C;
    }
    BigInteger operator += (const BigInteger &B){
        *this = *this + B;
        return *this;
    }
    bool operator < (const BigInteger &B) const{
        if(a.size() != B.a.size())  return a.size() < B.a.size();
        for(int i = a.size() - 1; i >= 0; i--)
            if(a[i] != B.a[i])
                return a[i] < B.a[i];
        return false;
    }
    bool operator > (const BigInteger &B) const{ return B < *this; }
    bool operator <= (const BigInteger &B) const{ return !(*this > B); }
    bool operator >= (const BigInteger &B) const{ return !(*this < B); }
    bool operator != (const BigInteger &B) const{ return (*this < B) || (*this > B); }
    bool operator == (const BigInteger &B) const{ return !(*this != B); }
    inline void print(){
        for(int i = a.size() - 1; i >= 0; i--)  printf("%d", a[i]);
        putchar(10);
    }
};

inline BigInteger max(BigInteger a, BigInteger b){ return a > b ? a : b; }
inline int min(int a, int b){ return a < b ? a : b; }
inline int max(int a, int b){ return a > b ? a : b; }

int n, K;
string S;
BigInteger dp[45][10], t;

int main(){
    scanf("%d%d", &n, &K);
    cin >> S;
    for(int i = 1; i <= n; i++)
        dp[i][0] = S.substr(0, i);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= K; j++){
            if(j >= i)  break;
            for(int k = j; k < i; k++){
                t = S.substr(k, i - k);
                dp[i][j] = max(dp[i][j], dp[k][j-1] * t);
            }
        }
    }
    dp[n][K].print();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章