洛谷 P1080 [NOIP 2012 提高组] 国王游戏 题解

洛谷 P1080 [NOIP 2012 提高组] 国王游戏 题解

题目描述

原题链接:洛谷 P1080 [NOIP 2012 提高组] 国王游戏

恰逢 H 国国庆,国王邀请 \(n\) 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 \(n\) 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

请帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。(\(1 \le n \le 1000,0 < a,b < 10000\))。

题意分析

本题的难点在于贪心的证明。

贪心的证明与排序的实现

假设有两名大臣,国王手上的整数分别为 \(a,b\),两名大臣手上的整数分别为 \(a_1,b_1\) 和 \(a_2,b_2\)。假设按当前的顺序站获得奖赏最多的大臣所获奖赏最少,即:

\[\max(\frac{a}{b_1},\frac{a\cdot a_1}{b_2})<\max(\frac{a}{b_2},\frac{a\cdot a_2}{b_1}) \]

化简,得:

\[\max(b_2,a_1\cdot b_1)<\max(b_1,a_2\cdot b_2) \]

由 \(a_1\cdot b_1>b_1,a_2\cdot b_2>b_2\),得:

\[a_1\cdot b_1<a_2\cdot b_2 \]

这就证明了这道题贪心的正确性:按照大臣左右手上的数的乘积升序排列结果最优。

那怎么贪心呢?直接用一个排序就完啦!下面是代码实现:

#include <iostream>
#include <algorithm> // 需要使用 sort 函数
using namespace std;

struct node {
    int a, b;
};

bool cmp(node x, node y) {
    return x.a * x.b < y.a * y.b;
}

int n;
node p[1005];
long long s, ans; // s 用来记录乘积,ans 表示获得奖赏最多的大臣获得的奖赏

int main() {
    cin >> n;
    for (int i = 0; i <= n; i++) // 不要忘了国王也要输入!
        cin >> p[i].a >> p[i].b;
    sort(p + 1, p + n + 1, cmp);
    
    s = p[0].a,  ans = 0;
    for (int i = 1; i <= n; i++) {
        long long now = s / p[i].b; // 记录大臣获得奖赏
        if (now > ans) ans = now; // 更新答案
        s = s * p[i].a;
    }
    
    cout << s << endl;
    return 0;
}

但很可惜,最终的答案可以达到 \(10000^{1000}\),long long 也存不下,所以还需要实现高精度。

高精度乘低精度、高精度除低精度、结构体的封装

这一部分在我之前的博客介绍过一部分,之前讲过的这里就不加注释了。

struct Bigint {
    int len, a[4100];

    Bigint(int x = 0) { // 使用构造函数初始化
        memset(a, 0, sizeof(a));
        for (len = 1; x != 0; len++)
            a[len] = x % 10, x /= 10;
        len--;
    }

    int &operator[](int i) { return a[i];}
    const int &operator[](int i) const { return a[i];}
    // 因为后面重载 > 运算符使用到 const 类型,所以也要重载 const 类型的 []

    void flatten(int L) { // 展平
        len = L;
        for (int i = 1; i <= len; i++)
            a[i + 1] += a[i] / 10, a[i] %= 10;
        while (a[len] == 0 && len > 0) len--;
    }

    void print() { // 输出
        for (int i = len; i >= 1; i--)
            printf("%d", a[i]);
    }
};

Bigint operator*(Bigint a, int b) { // 高精度乘低精度
    Bigint c;
    int len = a.len;
    for (int i = 1; i <= len; i++)
        c[i] = a[i] * b;
    c.flatten(len + 11);
    return c;
}

Bigint operator/(Bigint a, int b) { // 高精度除低精度
    Bigint c;
    int len = a.len, x = 0;
    for (int i = len; i >= 1; i--) { // 从最高位开始按位相除
        c[i] = (x * 10 + a[i]) / b;
        x = (x * 10 + a[i]) % b;
    }
    c.flatten(len);
    return c;
}

bool operator>(const Bigint &a, const Bigint &b) { // 重载 < 运算符
    // 因为直接传参速度慢,所以传入地址,并加上 const 防止原值被修改
    if (a.len != b.len) return a.len > b.len; // 先比较位数
    for (int i = a.len; i >= 1; i--)
        if (a[i] != b[i]) return a[i] > b[i]; // 如果位数相同,则按位比较
    return 0; // 如果两数相等,也返回 false
}

AC 代码

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

struct Bigint {
    int len, a[4100];

    Bigint(int x = 0) { // 使用构造函数初始化
        memset(a, 0, sizeof(a));
        for (len = 1; x != 0; len++)
            a[len] = x % 10, x /= 10;
        len--;
    }

    int &operator[](int i) { return a[i];}
    const int &operator[](int i) const { return a[i];}

    void flatten(int L) { // 展平
        len = L;
        for (int i = 1; i <= len; i++)
            a[i + 1] += a[i] / 10, a[i] %= 10;
        while (a[len] == 0 && len > 0) len--;
    }

    void print() { // 输出
        for (int i = len; i >= 1; i--)
            printf("%d", a[i]);
    }
};

Bigint operator*(Bigint a, int b) { // 高精度乘低精度
    Bigint c;
    int len = a.len;
    for (int i = 1; i <= len; i++)
        c[i] = a[i] * b;
    c.flatten(len + 11);
    return c;
}

Bigint operator/(Bigint a, int b) { // 高精度除低精度
    Bigint c;
    int len = a.len, x = 0;
    for (int i = len; i >= 1; i--) {
        c[i] = (x * 10 + a[i]) / b;
        x = (x * 10 + a[i]) % b;
    }
    c.flatten(len);
    return c;
}

bool operator>(const Bigint &a, const Bigint &b) { // 重载 < 运算符
    if (a.len != b.len) return a.len > b.len;
    for (int i = a.len; i >= 1; i--)
        if (a[i] != b[i]) return a[i] > b[i];
    return 0;
}

struct node {
    int a, b;
};

bool cmp(node x, node y) {
    return x.a * x.b < y.a * y.b;
}

int n;
node p[1005];
Bigint s, ans; // s 用来记录乘积,ans 表示获得奖赏最多的大臣获得的奖赏

int main() {
    cin >> n;
    for (int i = 0; i <= n; i++)
        cin >> p[i].a >> p[i].b;
    sort(p + 1, p + n + 1, cmp);
    
    s = p[0].a,  ans = 0; // 使用构造函数赋值
    for (int i = 1; i <= n; i++) {
        Bigint now = s / p[i].b; // 记录大臣获得奖赏,将 Bigint 类型值复制到 now 里面
        if (now > ans) ans = now; // 更新答案
        s = s * p[i].a;
    }
    
    s.print();
    return 0;
}
posted @ 2026-09-01 19:37  zzx321  阅读(26)  评论(0)    收藏  举报