更多Noip提高组题解:
题目来源
题目考点
贪心 高精度 排序
题目描述
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入格式
第一行包含一个整数 n,表示大臣的人数。
第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出格式
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
输入输出样例
输入 #1
3 1 1 2 3 7 4 4 6
输出 #1
2
说明/提示
【输入输出样例说明】
按 1、2、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 2、3、1这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;
按 3、1、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;
按 3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。
因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。
【数据范围】
对于 20% 的数据,有 1≤n≤10,0<a,b<8;
对于 40% 的数据,有1≤n≤20,0<a,b<8;
对于 60% 的数据,有 1≤n≤100;
对于 60% 的数据,保证答案不超过 ;
对于 100% 的数据,有 1≤n≤1,000,0<a,b<10000。
题解
看到这题标签:贪心。于是就想:怎么贪呢?
贪心
这道题70多行代码,其中的贪心只有5行,但这却是本题最难的部分。
开始推导:假设已经排了几个人(包括国王),设他们左手上的数的乘积为S。
现在要给2个人排序,记第一个人左手上的数为a1,右手上的数为b1;第二个人左手上的数为a2,右手上的数为b2。
如果第一个人排在前面优于第二个人排在前面,那么
max(S/b1,S∗a1/b2)<max(S/b2,S∗a2/b2)
而又由于a1,b1,a2,b2,S>0,所以 S/b2≤S∗a1/b2。
假如S∗a1/b2≥S∗a2/b1,则显然max(S/b1,S∗a1/b2)≥max(S/b2,S∗a2/b1),矛盾。
所以S∗a1/b2<S∗a2/b1=>a1∗b1<a2∗b2
只需将数组按照a*ba∗b从小到大排序即可。
高精度
这道题高精还是挺复杂的。
本题需要支持复制、比较、乘、除44种高精度操作(当然也有输出)。
-
复制:没什么好说的,一位一位赋值就好了。
-
比较:也没什么好说的,从高位到低位比较即可,注意是bool类型。
-
乘法:注意先从高到低乘一遍,再从低到高进位。
-
除法:要用一个数记录,和快读差不多。
代码
相信你们都会看到这里。。。
附上代码——
#include<cstdio>
#include<algorithm>//用到sort
#include<cstring>//用到memset
using namespace std;
const int MAXN=1010,MAXM=10010;//注意高精数组开到10000
struct Node{//一个人
int l,r;
}a[MAXN];
int pro[MAXM],ans[MAXM],tmp[MAXM];//左手乘积,答案,临时数组
int read(){//快读
int x=0,f=1;//记录数和符号
char c=getchar();//读入字符
while(c<'0'||c>'9'){//只要不是数
if(c=='-') f=-1;//是负号就记录
c=getchar();
}
while(c>='0'&&c<='9'){//只要是数
x=x*10+c-'0';//挪位再加
c=getchar();
}
return x*f;//返回数乘符号
}
bool cmp(Node aa,Node bb){//排序的比较函数
return aa.l*aa.r<bb.l*bb.r;//按左右手数的乘积从小到大
}
void copy(int *aa,int *bb){//复制
for(int i=0;i<MAXM;i++) aa[i]=bb[i];
}
bool more(int *aa,int *bb){//比较
for(int i=MAXM-1;i>=0;i--){
if(aa[i]>bb[i]) return 1;
if(aa[i]<bb[i]) return 0;
}
return 0;//注意这里也要写上,写0写1随便
}
void times(int *aa,int num){//乘法
for(int i=MAXM-2;i>=0;i--) aa[i]*=num;//先乘
for(int i=0;i<MAXM-1;i++){//再进位
aa[i+1]+=(aa[i]/10);//先加前一位
aa[i]%=10;//在处理这一位
}
}
void div(int *aa,int *bb,int num){//除法
memset(bb,0,sizeof(bb));//赋为0
int x=0;
for(int i=MAXM-1;i>=0;i--){//从高位到低位
x=x*10+aa[i];//挪位再加
bb[i]=x/num;//记录
x%=num;//模上
}
}
void print(int *aa){//输出
bool flag=0;//记录是否能输出
for(int i=MAXM-1;i>=0;i--){
if(!flag){//如果不能
if(aa[i]) flag=1;//找到第一个不是0的位,可以输出了
else continue;//还是不能
}
printf("%d",aa[i]);//输出,不用空格或换行
}
}
int main(){//主函数
int n=read();
for(int i=0;i<=n;i++) a[i].l=read(),a[i].r=read();
sort(a+1,a+n+1,cmp);//排序
pro[0]=1;//注意乘积数组初始值为1
for(int i=0;i<=n;i++){//注意从0开始,国王
div(pro,tmp,a[i].r);//先除到tmp上
if(more(tmp,ans)) copy(ans,tmp);//比较,满足就复制
times(pro,a[i].l);//自乘
}
print(ans);//输出
return 0;//华丽结束
}
总要点个赞再走呀~