题目大意
国王邀请 n 位大臣玩一个有奖游戏。每个大臣在左、右手上面分别写有一个整数,国王自己左、右手也各写一个整数。这 n 位大臣排成一排,国王始终站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。
输入描述
第一行包含一个整数 n ,表示大臣的人数。
第二行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
对于 100%的数据,有 1 ≤ n ≤1,000,0 < a,b < 10000 。
输出描述
输出一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
样例输入
3
1 1
2 3
7 4
4 6
样例输出
2
思路
我们贪心地考虑这个问题。由贪心的思想,两两之间的排列法则可以推广到所有元素。设局部排列(A,B)优于排列(B,A),且在此排列之前的所有大臣左手上的数乘积为 ∏ \prod ∏,则 m a x ( ∏ R A , ∏ × L A R B ) ≤ m a x ( ∏ R B , ∏ × L B R A ) max(\frac{\prod}{R_A},\frac{\prod×L_A}{R_B})≤max(\frac{\prod}{R_B},\frac{\prod×L_B}{R_A}) max(RA∏,RB∏×LA)≤max(RB∏,RA∏×LB),考虑到 ∏ × L A R B ≥ ∏ R B \frac{\prod×L_A}{R_B}≥\frac{\prod}{R_B} RB∏×LA≥RB∏,故 m a x ( ∏ R B , ∏ × L B R A ) = ∏ × L B R A max(\frac{\prod}{R_B},\frac{\prod×L_B}{R_A})=\frac{\prod×L_B}{R_A} max(RB∏,RA∏×LB)=RA∏×LB,而 ∏ R A ≤ ∏ × L B R A \frac{\prod}{R_A}≤\frac{\prod×L_B}{R_A} RA∏≤RA∏×LB必成立,故只需满足 ∏ × L A R B ≤ ∏ × L B R A \frac{\prod×L_A}{R_B}≤\frac{\prod×L_B}{R_A} RB∏×LA≤RA∏×LB,解得 L A R A ≤ L B R B L_AR_A≤L_BR_B LARA≤LBRB,故只需要根据 L 与 R 乘积由小到大进行排序即可。
需要注意数据范围1 ≤ n ≤1,000,0 < a < 10000,故 ∏ \prod ∏最大可达 1 0 4 × 1000 = 1 0 4000 10^{4×1000}=10^{4000} 104×1000=104000,显然需要使用高精度计算。使用一个数组储存当前的大数,大数每次乘以 a 这一较小的数;再利用另一个数组储存当前大数除以 较小的数 b 后的值,比较这一值与答案的大小,并不断更新答案,答案也用数组存储。故需要使用大数乘较小数的高精度乘法和大数除较小数的高精度除法。高精度乘法和除法的操作即分别模拟乘法竖式和除法竖式,对乘得的每一位进位和对当前被除数的值更新。具体操作细节见代码。
考点
贪心
高精度
AC代码(含注释)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e3+5;
int now[4*maxn],ans[4*maxn],afe[4*maxn];//注意开大数组
int len=1,bit=0,culen=1;
struct Node{
int a,b;
bool friend operator < (const Node &A, const Node &B){
return A.a*A.b<B.a*B.b;//按左右手乘积从小到大排序
}
}node[maxn];
void mul(int x){//高精度乘法
for(int i=1;i<=len;i++)now[i]*=x;//每一位都乘以该数
for(int i=1;i<=len;i++)now[i+1]+=now[i]/10,now[i]%=10;//进位
if(now[len+1])len++;//进位后首位加长
while(now[len]>10)now[len+1]+=now[len]/10,now[len]%=10,len++;//首位继续加长
}
void exc(int x){//高精度除法
int cur=0,rem=0,f=0;bit=0;
for(int i=len;i>=1;i--){//模拟除法竖式
cur=now[i]+rem*10;//计算当前被除数的值,rem是余数
if(!f&&cur<x)rem=cur;//还没有出现过非零位且该位也为0,余数即为被除数
else f=1,afe[++bit]=cur/x,rem=cur%x;//记录该位上的数并更新余数
}
if(!bit)afe[1]=0,bit=1;//如果作除后得到的数位数为0,说明作除后得到的值为0
}
void upd(){//更新答案
if(bit>culen){//当前大数的位数大于答案位数,则当前数一定大于答案
for(int i=1;i<=bit;i++)ans[i]=afe[i];//更新答案数值
culen=bit;//更新答案位数
}
else if(bit==culen){//当前大数的位数等于答案的位数
for(int i=1;i<=bit;i++){
if(afe[i]>ans[i]){//如果当前位该数要比答案大
for(int i=1;i<=bit;i++)ans[i]=afe[i];//更新答案数值
break;
}
}
}
}
signed main(){
int n;
cin>>n;
cin>>node[0].a>>node[0].b;//国王
for(int i=1;i<=n;i++)cin>>node[i].a>>node[i].b;//大臣
sort(node+1,node+1+n);//结构体排序
now[1]=1,ans[1]=0;
for(int i=1;i<=n;i++){
mul(node[i-1].a);//累乘至前一位大臣左手
exc(node[i].b);//除以当前大臣右手
upd();//更新答案
}
for(int i=1;i<=culen;i++)cout<<ans[i];//打印答案
return 0;
}
心得
国王的游戏是一道经典的贪心题目,由贪心思想得到的最终结论是需要严格的证明才可得到,得出结论的过程考察了对贪心思想量化的具体应用,是最接近贪心原始思想的一道好题。同时还考察了高精度算法,对于大数的计算和答案更新方面有着较为复杂的细节。本题非常值得仔细研究并全面掌握。