洛谷:P1080
OJ:P5967
题目分析:
本题满分需要高精乘法和除法:
当 N 较大时,左手数字的乘积可能会非常大。即使单个左手数不大,经过多次乘法后,乘积会快速增长,导致无法使用标准的 int 或 long long 类型来存储这些中间结果。
我们考虑两个大臣p1,p2,它们站在国王p0的身后,则这两个大臣有两种排列方案:
方案一: p1站在p2的前面
| person | left | right |
|---|---|---|
| p0 | a0 | b0 |
| p1 | a1 | b1 |
| p2 | a2 | b2 |
方案二:p1站在p2的后面
| person | left | right |
|---|---|---|
| p0 | a0 | b0 |
| p2 | a2 | b2 |
| p1 | a1 | b1 |
对于第一种情况,答案 ans1=max(\(\frac{a_{0}}{b_{1}}\), \(\frac{a_{0}a_{1}}{b_{2}}\))
对于第二种情况,答案 ans2=max(\(\frac{a_{0}}{b_{2}}\), \(\frac{a_{0}a_{2}}{b_{1}}\))
显然,\(\frac{a_{0}}{b_{2}}\) < \(\frac{a_{0}a_{1}}{b_{2}}\) , \(\frac{a_{0}}{b_{1}}\)< \(\frac{a_{0}a_{2}}{b_{1}}\)。
若ans1<ans2, \(\frac{a_{0}a_{1}}{b_{2}}\)< \(\frac{a_{0}a_{2}}{b_{1}}\)
推导见下表:
| ans1<ans2 | max(\(\frac{a_{0}}{b_{1}}\), \(\frac{a_{0}a_{1}}{b_{2}}\)) | ans2=max(\(\frac{a_{0}}{b_{2}}\), \(\frac{a_{0}a_{2}}{b_{1}}\)) | 判断 |
| 1 | \(\frac{a_{0}}{b_{1}}\) | \(\frac{a_{0}}{b_{2}}\) | 不可能,因为 \(\frac{a_{0}a_{1}}{b_{2}}\)>\(\frac{a_{0}}{b_{2}}\) |
| 2 | \(\frac{a_{0}}{b_{1}}\) | \(\frac{a_{0}a_{2}}{b_{1}}\) | 推导可知\(\frac{a_{0}a_{1}}{b_{2}}\)< \(\frac{a_{0}a_{2}}{b_{1}}\) |
| 3 | \(\frac{a_{0}a_{1}}{b_{2}}\) | \(\frac{a_{0}}{b_{2}}\) | 不可能 |
| 4 | \(\frac{a_{0}a_{1}}{b_{2}}\) | \(\frac{a_{0}a_{2}}{b_{1}}\) | \(\frac{a_{0}a_{1}}{b_{2}}\)< \(\frac{a_{0}a_{2}}{b_{1}}\) |
结论:ans1<ans2, \(\frac{a_{0}a_{1}}{b_{2}}\)< \(\frac{a_{0}a_{2}}{b_{1}}\),即:\(\frac{a_{1}}{b_{2}}\) < \(\frac{a_{2}}{b_{1}}\), a1b1<a2b2。
若 a1b1<a2b2 , p1就应该排在p2前更优,也就是最大值更小。
如果p0和p1之间还有若干大臣,也不影响p1和p2之间的位置关系做带来的结果。
因此,我们得出结论:如果aibi<ajbj,那么应当让i排在j的前方,即按照ai*bi的大小从小到大排序。
代码一: 排序+普通乘法 60分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2025-10-14 15:32:36 * 最后修改: 2025-10-14 16:24:13 * 文件描述:国王游戏 不用高精度乘法 ****************************************************************/ #include <bits/stdc++.h> using namespace std; using ll = long long; struct Node { ll a, b; }; // 比较 a1*b1 与 a2*b2(尽量不用高精) bool cmp(const Node& X, const Node& Y){ if (X.a * X.b != Y.a * Y.b) return X.a * X.b < Y.a * Y.b; return X.a < Y.a;// 次关键字(可选):按 a 升序 } int main(){ int n; ll kingA, kingB; cin>>n; cin >> kingA >> kingB; vector<Node> v(n); for (int i = 0; i < n; ++i) cin >> v[i].a >> v[i].b; sort(v.begin(), v.end(), cmp); ll P = kingA; // 前缀乘积从国王左手开始 ll ans = 0; for (int i = 0; i < n; ++i) { ll cur = P /v[i].b;; // 计算当前人的赏金 if (cur > ans) ans = cur; P *= v[i].a; // 前缀乘积乘上 a_i } cout << ans << "\n"; return 0; } |
代码实现二:用sort+pair+高精度乘法和除法 100分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | #include <iostream> #include <string.h> #include <vector> #include <algorithm> using namespace std; int N; // 大臣人数 pair<int, int> king; // 国王的左右手数 vector<pair<int, int>> ministers; // 存储每个大臣的左右手数 int ans[20000], t[20000], lena, lent, tt[20000], t2[20000], len; // 高精度计算用的数组 // 比较函数,用于排序大臣的乘积 bool compareMinisters(const pair<int, int>& a, const pair<int, int>& b) { return a.first * a.second < b.first * b.second; } // 初始化函数,读取输入 void initialize() { cin >> N >> king.first >> king.second; // 读取国王的左右手数 ministers.resize(N); // 调整大臣数据的大小 for (int i = 0; i < N; i++) { cin >> ministers[i].first >> ministers[i].second; // 每个大臣的左右手数 } } // 计算当前大臣的奖赏 void calculateReward(int leftProduct, int currentRight) { for (int i = 1; i <= lent; i++) { tt[i] += t[i] * leftProduct; // 更新乘积 tt[i + 1] += tt[i] / 10; // 处理进位 tt[i] %= 10; } lent++; while (tt[lent] >= 10) { tt[lent + 1] = tt[lent] / 10; tt[lent] %= 10; lent++; } while (lent > 1 && tt[lent] == 0) lent--; // 去除前导零 len = lent; memcpy(t, tt, sizeof(tt)); // 复制结果 memset(tt, 0, sizeof(tt)); for (int i = 1, j = len; i <= len; i++, j--) t2[i] = t[j]; // 逆序 int carry = 0, remainder = 0; for (int i = 1; i <= len; i++) { remainder = carry * 10 + t2[i]; tt[i] = remainder / currentRight; carry = remainder % currentRight; } int startIndex = 1; while (startIndex < len && tt[startIndex] == 0) startIndex++; memset(t2, 0, sizeof(t2)); for (int i = 1, j = startIndex; j <= len; i++, j++) t2[i] = tt[j]; memcpy(tt, t2, sizeof(t2)); len = len + 1 - startIndex; } // 比较当前计算结果是否更优 bool isBetter() { if (len > lena) return true; if (len < lena) return false; for (int i = 1; i <= len; i++) { if (ans[i] < tt[i]) return true; if (ans[i] > tt[i]) return false; } return false; } // 核心求解函数 void solve() { // 使用 std::sort 对大臣按乘积排序 sort(ministers.begin(), ministers.end(), compareMinisters); t[1] = 1; lent = 1; // 初始化乘积为1 for (int i = 0; i < N; i++) { memset(tt, 0, sizeof(tt)); // 清空临时数组 len = 0; int leftProduct = (i == 0) ? king.first : ministers[i - 1].first; int currentRight = ministers[i].second; calculateReward(leftProduct, currentRight); // 计算当前大臣的奖赏 if (isBetter()) { // 如果当前结果更优,则更新 memcpy(ans, tt, sizeof(tt)); lena = len; } } // 输出最终答案 for (int i = 1; i <= lena; i++) { cout << ans[i]; } cout << endl; } int main() { freopen("game.in","r",stdin); freopen("game.out","w",stdout); initialize(); // 读取输入 solve(); // 求解并输出结果 return 0; } |