P1018 乘积最大

2020-05-28  本文已影响0人  yuq329

P1018 乘积最大

题目描述

今年是国际数学联盟确定的“20002000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:

设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。

同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

有一个数字串:312, 当N=3,K=1时会有以下两种分法:

1、3 \times 12=36
2、31 \times 2=62

这时,符合题目要求的结果是: 31 \times 2 = 62

现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入格式

程序的输入共有两行:

第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)

第二行是一个长度为N的数字串。

输出格式

结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。

输入输出样例

输入

4 2
1231

输出

62

说明/提示

NOIp2000提高组第二题

解题思路一

这题可以使用深度优先遍历做,根据左边数字的划分位置,递归求右侧划分k-1次可以得到的最大乘积,然后与左边数字相乘,寻找最大值。
例如可以先将 1231 划分为 1231 ,再计算 231 可以经过划分得到的最大值,这里可以看出最大值为 62 .

这题还有一个问题就是数值范围太大,如果不使用高精度计算,只能通过60%。
高精度计算可以参考另一篇博文:https://www.jianshu.com/p/85e3220b011c

#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<iomanip>

using namespace std;
const int power = 4; //压位
const int base = 10000;
const int LEN = 105;

class UNum {
public:
    int a[LEN + 1];

    //清空数字
    void clear() {
        for (int i = 0; i <= LEN; ++i) a[i] = 0;
    }

    UNum() {
        clear();
    }

    UNum(const char *s, int len) {
        this->clear();
        //a[0]保存数字长度,此时一个int保存一个4位长度的数字
        a[0] = (len + power - 1) / power;
        for (int t = 1; t <= a[0]; ++t) {
            for (int i = max(len - t * power, 0); i < min(len, len - t * power + power); ++i) {
                a[t] = 10 * a[t] + (s[i] - '0');
            }
        }
    }

    friend bool operator<(const UNum &p, const UNum &q) {
        if (p.a[0] < q.a[0]) return true;
        if (p.a[0] > q.a[0]) return false;
        for (int i = p.a[0]; i > 0; --i) {
            if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
        }
        return false;
    }

    friend UNum operator*(const UNum &p, const UNum &q) {
        UNum c;
        c.a[0] = p.a[0] + q.a[0] - 1;
        for (int i = 1; i <= p.a[0]; ++i)
            for (int j = 1; j <= q.a[0]; ++j) {
                c.a[i + j - 1] += p.a[i] * q.a[j];
                c.a[i + j] += c.a[i + j - 1] / base;
                c.a[i + j - 1] %= base;
            }
        if (c.a[c.a[0] + 1]) ++c.a[0];
        return c;
    }

    friend ostream &operator<<(ostream &os, const UNum &num) {
        int i = num.a[0];
        cout << num.a[i--];
        for (; i > 0; --i) cout << setw(power) << setfill('0') << num.a[i];
        cout << '\n';
        return os;
    }
};


class Key {
public:
    int start = 0;
    int end = 0;
    int k = 0;

    friend ostream &operator<<(ostream &o, const Key &key) {
        o << '(' << key.start << ',' << key.end << ',' << key.k << ')';
        return o;
    }

    Key(int s, int e, int k) : start(s), end(e), k(k) {
    }

    bool operator==(const Key &key) const {
        if (this == &key)
            return true;
        return this->start == key.start && this->end == key.end && this->k == key.k;
    }
};


namespace std {
    template<>
    struct hash<Key> {
        size_t operator()(const Key &s) const noexcept {
            return hash<int>()(s.start) + hash<int>()(s.end) + hash<int>()(s.k);
        }
    };
}


UNum dfs(const char *list, int start, int end, int k, unordered_map<Key, UNum> &rem) {
    if (k == 0) {
        UNum m(list + start, end - start + 1);
        return m;
    }
    if (start == end || (end - start) < k)
        return UNum();
    if (rem.find(Key(start, end, k)) != rem.end()) {
        return rem[Key(start, end, k)];
    }
    UNum m;
    for (int i = start; i < end; ++i) {
        UNum left(list + start, i - start + 1);
        if (rem.find(Key(i + 1, end, k - 1)) == rem.end()) {
            rem.insert(make_pair(Key(i + 1, end, k - 1), dfs(list, i + 1, end, k - 1, rem)));
        }
        UNum right = rem[Key(i + 1, end, k - 1)];
        m = max(m, left * right);
    }
    return m;
}

int main() {
    int N = 0, K = 0;
    cin >> N >> K;
    
    //这里直接开N大小,有可能通不过,不知道为啥,本地能通过
    //看来在最开始直接开最大最好 emmm
    char *numbers = new char[N + 2];
    cin >> numbers;
    unordered_map<Key, UNum> rem;
    UNum res = dfs(numbers, 0, N - 1, K, rem);
    cout << res;
    delete[] numbers;
    return 0;
}

解题思路二

可以利用动态规划,f[i][j]表示前 j 位添加了 i 个乘号时的最优解, s 表示原字符串。
状态转移方程f[i][j]=max(f[i][j],f[i-1][v-1]*s(v,j));
参考链接:https://www.luogu.com.cn/blog/14378/solution-p1018


#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<iomanip>

using namespace std;
const int power = 4; //压位
const int base = 10000;
const int LEN = 105;

class UNum {
public:
    int a[LEN + 1];

    //清空数字
    void clear() {
        for (int i = 0; i <= LEN; ++i) a[i] = 0;
    }

    UNum() {
        clear();
    }

    UNum(const char *s, int len) {
        this->clear();
        //a[0]保存数字长度,此时一个int保存一个4位长度的数字
        a[0] = (len + power - 1) / power;
        for (int t = 1; t <= a[0]; ++t) {
            for (int i = max(len - t * power, 0); i < min(len, len - t * power + power); ++i) {
                a[t] = 10 * a[t] + (s[i] - '0');
            }
        }
    }

    UNum &operator=(const UNum &num) {
        if (this == &num) return *this;
        this->clear();
        for (int i = 0; i <= num.a[0]; ++i) this->a[i] = num.a[i];
        return *this;
    }

    friend bool operator<(const UNum &p, const UNum &q) {
        if (p.a[0] < q.a[0]) return true;
        if (p.a[0] > q.a[0]) return false;
        for (int i = p.a[0]; i > 0; --i) {
            if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
        }
        return false;
    }

    friend UNum operator*(const UNum &p, const UNum &q) {
        UNum c;
        c.a[0] = p.a[0] + q.a[0] - 1;
        for (int i = 1; i <= p.a[0]; ++i)
            for (int j = 1; j <= q.a[0]; ++j) {
                c.a[i + j - 1] += p.a[i] * q.a[j];
                c.a[i + j] += c.a[i + j - 1] / base;
                c.a[i + j - 1] %= base;
            }
        if (c.a[c.a[0] + 1]) ++c.a[0];
        return c;
    }

    friend ostream &operator<<(ostream &os, const UNum &num) {
        int i = num.a[0];
        cout << num.a[i--];
        for (; i > 0; --i) cout << setw(power) << setfill('0') << num.a[i];
        cout << '\n';
        return os;
    }
};

char s[LEN];
UNum f[LEN][LEN];

int main() {
    int N = 0, K = 0;
    cin >> N >> K >> s;
    for (int i = 0; i <= N; ++i) {
        f[0][i] = UNum(s, i+1);
    }

    for (int i = 1; i <= K; i++)
        for (int j = 1; j <= N; j++) {
            for (int v = j; v >= i; v--) {
                f[i][j] = max(f[i][j], f[i - 1][v - 1] * UNum(s + v, j - v + 1));
            }
        }
    cout << f[K][N-1];
    return 0;
}
上一篇 下一篇

猜你喜欢

热点阅读