[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 ( C N K ) ,记忆化之后貌似还可以过。
不过这是一道经典的dp问题(毕竟记忆化搜索和dp本质是一样的):

  • dp状态: d p [ i ] [ j ] 表示前 i 位数字中插入 j 个乘号能得到的最大值
  • dp方程: d p [ i ] [ j ] = m a x {   d p [ k ] [ j 1 ] A k + 1... i     |     j k < i   } ,其中 A i . . . j 表示原数字中第 i 位到第 j 位组成的数字
  • dp顺序:从dp方程易知, i , j , k 从小到大依次循环即可,一定要注意范围
  • 边界条件: d p [ i ] [ 0 ] = A 1... i ,即前 i 位一个乘号也不加就是它本身
    答案就是 d p [ 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;
}

猜你喜欢

转载自blog.csdn.net/phantomagony/article/details/79188866
今日推荐