题面
题目描述
恰逢(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\%)的数据,有(1le nle 10),(0<a,b<8);
对于(40\%)的数据,有(1le nle 20),(0<a,b<8);
对于(60\%)的数据,有(1le nle 100);
对于(60\%)的数据,保证答案不超过(10^9);
对于(100\%)的数据,有(1 le n le 1,000),(0 < a,b < 10000)。
NOIP 2012 提高组 第一天 第二题
分析
这道题看似挺混乱,一堆大臣,一个国王,左手右手……蒙圈,死亡。
不过我最想吐槽的是国王在左手写个0不就可以达到目的了吗
算了,咱们先别看一堆大臣,先看国王和他后面的两个大臣,分析一下吧。
列个表:
身份 | 左手 | 右手 |
---|---|---|
国王 | (a_0) | (b_0) |
大臣1 | (a_1) | (b_1) |
大臣2 | (a_2) | (b_2) |
分析一下,大臣1可以获得多少金币呢?
很明显是(frac{a_0}{b_1}),
大臣2能获得多少金币呢?
(frac{a_0*a_1}{b_2}),
这个时候,两个大臣中最多的金币数我们记为(ans_1),那么显然有
好现在我们交换一下大臣1和大臣2。
身份 | 左手 | 右手 |
---|---|---|
国王 | (a_0) | (b_0) |
大臣2 | (a_2) | (b_2) |
大臣1 | (a_1) | (b_1) |
这回呢,大臣1可以获得(frac{a_0*a_2}{b_1}),
大臣2可以获得(frac{a_0}{b_2})。
这个时候两个大臣中最多的金币数我们记为(ans_2),那么显然有
$ ans_2 = max(frac{a_0*a_2}{b_1},frac{a_0}{b_2}) $。
放在一起对照一下:
题目中已经说了,所有大臣左右手的数字都是整数。
那么非常显然,(frac{a_0}{b_1} le frac{a_0*a_2}{b_1})且(frac{a_0}{b_2} le frac{a_0*a_1}{b_2})。
但是用这么又臭又长的一坨式子带入简直丧心病狂。怎么办呢?一个字,设!
一顿狂设,(frac{a_0}{b_1})设为(p_1),(frac{a_0*a_1}{b_2})设为(p_2),(frac{a_0*a_2}{b_1})设为(p_3),(frac{a_0}{b_2})设为(p_4)。
那么就有(p_1 le p_3),(p_4 le p_2)。
这式子瞬间清爽了许多。
如果我们想让(ans_1)为最优解,也就是说(ans_1 le ans_2),需要怎么做?
啧啧啧,看起来不太好办,,,
没关系,我们可以把(p_1,p_2,p_3,p_4)放在一起,小火慢炖求他们的最大值。
你看哈,(p_1 le p_3),(p_4 le p_2),
所以最大值一定可以用非(p_1),(p_4)的方法表示,我们排除。
还剩下来自(ans_1)的(p_2)和来自(ans_2)的(p_3)。
但是我们已经说要保证(ans_1 le ans_2),所以我们必须让(p_2 le p_3)才能保证这个结果。
好的,我们得到了一个贪心的原则:(p_2 le p_3)。
至此(p)的使命已经结束,做一个告别,我们逐渐换回原式。
我们在这里两边同时乘上(frac{b_1*b_2}{a_0})。
到这里,一切都很明朗了,我们只需要按照这个关系,贪心排序就ok
。
但还需要注意的一点是,从数据范围可以很容易看出,这种东西卡ull
!cpp
选手失声痛哭,我们只能用高精度了!
还需要注意的一点是,最后一名大臣确实吃香,但不要直接输出最后一名大臣,你没有办法证明其正确性。这种方法可以拿90分。
再再需要注意的一点,国王永远在第一个位置,不能包括在贪心的排序中。因为这点,我拿了80分的好成绩。
重要点都说完了,咱们上代码吧。
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-02-05 00:19:09
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-02-05 00:56:58
*/
#include <bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=1005;
const int maxh=20005;
struct gamer{
long l,r;
bool operator<(const gamer a) const{
return l*r<a.l*a.r;
}
}gamers[maxn];
string mul(string a,string b){
int alen=a.size();
int blen=b.size();
int res[maxh]={0};
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
for(int i=0;i<alen;i++)
for(int j=0;j<blen;j++)
res[i+j]+=(a[i]-'0')*(b[j]-'0');
for(int i=0;i<alen+blen;i++)
if(res[i]>=10){
res[i+1]+=res[i]/10;
res[i]%=10;
}
string ret="";
bool frontzero=true;
for(int i=alen+blen-1;i>=0;i--){
if(!res[i]&&frontzero) continue;
frontzero=false;
ret+=res[i]+'0';
}
return ret;
}
string div(string a,int b){
int res[maxh]={0},resa[maxh]={0};
int len=a.size();
for(int i=0;i<len;i++)
res[i+1]=a[i]-'0';
int tmp=0;
for(int i=1;i<=len;i++){
resa[i]=(tmp*10+res[i])/b;
tmp=(tmp*10+res[i])%b;
}
int start=1;
string ret="";
while(!resa[start]&&start<len) start++;
for(int i=start;i<=len;i++)
ret+=resa[i]+'0';
return ret;
}
string max(string a,string b){
if(a.size()!=b.size()) return a.size()>b.size()?a:b;
return a>b?a:b;
}
string i_to_s(int x){
string ret="";
do{
ret+=x%10+'0';
}while(x/=10);
reverse(ret.begin(),ret.end());
return ret;
}
int main(){
scanf("%d",&n);
for(int i=0;i<=n;i++)
scanf("%ld%ld",&gamers[i].l,&gamers[i].r);
sort(gamers+1,gamers+n+1);
string ans="0";
string tmp=i_to_s(gamers[0].l);
for(int i=1;i<=n;i++){
ans=max(ans,div(tmp,gamers[i].r));
tmp=mul(tmp,i_to_s(gamers[i].l));
}
cout<<ans<<endl;
return 0;
}
评测结果
over.