题目链接
题目梗概
现在有一位国王和N位大臣,排成一个队列。每个人左右手中各有整数a,b。队列中的每位大臣都可以获得国王奖励的金币,第i个大臣获得的金币数为 a(king)*a(1)*a(2)*…*a(i-1) / b(i)
。
所有大臣得到的金币数不完全一样,金币数肯定有最大值。
重新排列这些大臣的顺序,会改变这些大臣所得到的金币数,最大值也会有可能改变。
求在所有排列的最大金币值中,最小的是多少。
解题思路
贪心策略
本题的贪心策略比较不容易想到。我们可以像平常排列问题一样,先假定根据某种最优策略交换队列中某两个人位置后,得到的金币最大值更小。
此时,为简化问题便于分析,我们再进行一次假设,假定交换前后的两种队列的金币最大值都是在这两个人中,这样可以忽略到其他人。我们再计算出交换前两个人的金币数和交换后两个人的金币数,通过大小关系比较,可以得到ai*bi小的排在前面,金币最大值会更小。
贪心策略得到了,但不免想到,金币最大值肯定不总是在交换的两个人得到,那种情况下,上述的贪心策略还能够应用么。此时,我们可以对贪心策略进行证明,先假定队列已经按上述的贪心策略排列好,再推理得到交换前后各种情况的金币最大值,进行比较,贪心策略可以得证。
一个启示,在寻找贪心策略时,多做一些特殊性假设,某种程度上会易于得到贪心策略。
高精度问题
本题中,数据规模最大时,N为1000,a,b为10000,可以到在计算ai的乘积时,会超过long long 的数据规模,需要用数组来存储数字,此时就要涉及高精度(大数)的乘除法,即数组操作。
本题的高精度乘法为高精度乘以单精度,即大数(数组存储)乘以一个正常大小的数(int等数据类型)。方法可以结合下图自行理解。
除法则模拟手算除法中的竖式除法即可。
完整代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct minister{
int left, right;
long long left_mul_right;
};
vector<int> product,coin,ans;
void mul(int x){
//记录进位
int tmp = 0;
//各个位置的数码直接乘以乘数X
for(int i = 0;i < product.size();++i) product[i] *= x;
//处理进位
for(int i = 0;i < product.size();++i){
product[i] += tmp;
tmp = product[i]/10;
product[i] %= 10;
if(i == product.size()-1 && tmp != 0){
product.push_back(0);
}
}
}
void div(int x){
//初始化 商,默认长度等于被除数长度
coin.clear();
coin.resize(product.size(),0);
//记录当前被除数
int tmp = 0;
for(int i = product.size()-1;i>=0;--i){
tmp *= 10;
tmp += product[i];
//得到商和余
if(tmp >= x){
coin[i] = tmp / x;
tmp %= x;
}
}
//去除最高位的0
while(coin.size() != 1 && coin.back()==0) coin.pop_back();
}
bool gt(vector<int> &a, vector<int> &b){
if(a.size() != b.size()) return a.size() > b.size();
else{
for(int i = a.size() - 1;i >= 0;--i){
if(a[i] != b[i]) return a[i] > b[i];
}
return true;
}
}
bool cmp(minister a, minister b){
return a.left_mul_right < b.left_mul_right;
}
int main(){
int n;
cin >> n;
minister king;
cin >> king.left >> king.right;
vector<minister> m(n);
for(int i = 0;i<n;++i){
cin >> m[i].left >> m[i].right;
m[i].left_mul_right = m[i].left * m[i].right;
}
sort(m.begin(),m.end(),cmp);
while(king.left){
product.push_back(king.left%10);
king.left /= 10;
}
for(int i = 0;i<n;i++){
div(m[i].right);
mul(m[i].left);
if(gt(coin, ans)) ans = coin;
}
for(int i = ans.size()-1;i >= 0;--i) cout << ans[i];
return 0;
}