AcWing 114.国王游戏

题目传送门(Luogu)

题目传送门(AcWing)

一、公式推导

我们对于国王身后的两个点来分析

队列可能是这样的:

(*) 左手 右手
国王 (a_0) (b_0)
(p1) (a_1) (b_1)
(p2) (a_2) (b_2)

根据题意:每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

那么我们计算可得当前情况下大臣的最大金币数量: (large ans_1=max(frac{a_0}{b_1},frac{a_0*a_1}{b_2}))

队列也有可能是这样的

(*) 左手 右手
国王 (a_0) (b_0)
(p2) (a_2) (b_2)
(p1) (a_1) (b_1)

那么我们计算可得 (large ans_2=max(frac{a_0}{b_2},frac{a_0*a_2}{b_1}))

我们来对比一下两个答案:
(large ans_1=max(frac{a_0}{b_1},frac{a_0 * a_1}{b_2}))

(large ans_2=max(frac{a_0}{b_2},frac{a_0 * a_2}{b_1}))


(large k_1=frac{a_0}{b_1})
(large k_2=frac{a_0 * a_1}{b_2})
(large k_3=frac{a_0}{b_2})
(large k_4=frac{a_0 * a_2}{b_1})

因为(a_0*a_1>a_0),所以 (frac{a_0 * a_1}{b_2}>frac{a_0}{b_2}),即 (k_2>k_3)

因为(a_0*a_2>a_0),所以 (frac{a_0 * a_2}{b_1}>frac{a_0}{b_1}),即 (k_4>k_1)

如果想要 (ans_1<ans_2),那么 (max(k_1,k_2) < max(k_3,k_4)),因为上面已经知道(k_2>k_3), 所以 $k_4 > k_2 $

即: (frac{a_0 * a_2}{b_1}> frac{a_0 * a_1}{b_2})

不等式变型

(a_0 * a_2 * b_2 > a_0 * a_1 * b_1)

消去(a_0)

(a_2 * b_2 > a_1 * b_1)

也就是:
(a_1 * b_1 < a_2 * b_2)

也就是说,当 (a_1 * b_1 < a_2 * b_2)时,可以得到 (ans_1 <ans_2),也就是为了使(ans)更小,需要将(a_i * b_i)较小的放在前面,我们以(a_i * b_i)为关键字排序即可。

同时,由于数据范围是:
(n=1000),同时 (a<=10000),连乘就是 (1000)(10000)相乘,需要用到高精度。

二、简单版本代码

#include <bits/stdc++.h>

using namespace std;
const int N = 10010;
typedef long long LL;
struct Person {
    int left, right;
} person[N];
int n;

bool cmp(const Person &a, const Person &b) {
    return a.left * a.right < b.left * b.right;
}

int main() {
    cin >> n;
    //输入
    for (int i = 0; i <= n; i++)
        cin >> person[i].left >> person[i].right;
    //排序,注意国王不参加排序
    sort(person + 1, person + 1 + n, cmp);

    LL lcj = 1;//连乘积
    LL res = 0;
    for (int i = 0; i <= n; i++) {
        //猴子选大王
        res = max(res, lcj / person[i].right);
        //更新连乘各
        lcj *= person[i].left;
    }
    printf("%d", res);
    return 0;
}

三、高精度版本代码

#include<bits/stdc++.h>

using namespace std;

const int N = 1010;
int n;
struct Person {
    int left, right;
} person[N];

bool cmp(const Person &a, const Person &b) {
    return a.left * a.right < b.left * b.right;
}

//高精度乘法
vector<int> mul(vector<int> &A, int b) {
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i++) {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

//高精度除法
vector<int> div(vector<int> &A, int b, int &r) {
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i--) {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

//获取两个vector<int>中较大的那个
vector<int> max_vec(vector<int> a, vector<int> b) {
    if (a.size() > b.size()) return a;
    if (a.size() < b.size()) return b;
    for (int i = a.size() - 1; i >= 0; i--) {
        if (a[i] > b[i]) return a;
        if (a[i] < b[i]) return b;
    }
    return a;
}

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    cin >> n;
    //输入
    for (int i = 0; i <= n; i++)
        cin >> person[i].left >> person[i].right;

    //排序,国王不参加排序
    sort(person + 1, person + n + 1, cmp);

    //连乘各放入国王
    vector<int> lcj;
    lcj.push_back(person[0].left);

    //结果
    vector<int> res;
    res.push_back(0);//在做高精度时,也需要进行一步初始化

    for (int i = 1; i <= n; i++) {
        int r;//余数,本题中因为只需要下取整,所以余数无意义
        res = max_vec(res, div(lcj, person[i].right, r));
        lcj = mul(lcj, person[i].left);
    }
    //倒序输出高精度数组中的值
    for (int i = res.size() - 1; i >= 0; i--)
        printf("%d", res[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/littlehb/p/15038424.html