洛谷 P1080 [NOIP2012 提高组] 国王游戏

今天在洛谷上写了这道绿题,刚开始感觉看起来好简单以为是捡漏了,写的时候才发现这不仅是一道贪心题,这更是一道高精题(思考高精运算时间>>思考贪心时间),很新奇,所以就忽然间想来发一篇文章来讲讲这道题。

题目如下懒得看的直接往下翻一点

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

 输入格式

第一行包含一个整数 n,表示大臣的人数。

第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

大概意思就是有很多人排队,每个人左手右手上都有一个数字,然后每个人得到的金币等于他之前所有人的左手的数字乘积再除以他自己右手的数字。然后我们要将他们(除了第一个人)排序,使得获得最多金币的那个人获得的金币最少(有点像最小的最大值)

那我们先来列举一下前两个:

设前三个人(国王,大臣A,大臣B)左右手上的数字分别为a0,b0,a1,b1,a2,b2,如果大臣A在前面,那么他们俩获得的金币分别为:a0/b1,a0*a1/b2。如果反过来,那么他们俩获得的金币就变成:a0/b2,a0*a2/b1。所以其实就是比较这四个数字的大小,然后因为a0>0,所以可以先约掉,又因为a>=1,所以最后变成只要比较a1/b2a2/b1的大小,那就被转换成了比较a1*b1a2*b2的大小,那正常来说,就只剩下写一个sort来解决这题了。但是!!这题有坑!!

后面的a,b,n的数字组合起来是特别大的(远远爆long long了),所以这题就要用高精(万恶之源来计算看看上面的分析,我们知道我们要对数据进行乘除,所以这题我们要手搓一个乘除卡了我半天(꒪⌓꒪)

如果大佬们写这题是高精乘除已经掌握的很好了,那这题依旧简单

那接下来就来看看高精乘除咋写吧

首先,迎面向我们走来的是高精乘法,我们先想一想我们平时写大数字相乘时是怎么乘的不是计算机,我们是不是先拿一个数字的个位数去乘另一个数字的所有数字,所以我们自己手搓乘法时也要一样(但是要注意的是我们使用字符串来进行操作的,而不是直接数字)

先献上代码

string hmul(string a,string  b){//高精乘法
    int c[500005];//用来储存数字的
    memset(c,0,sizeof c);//用0来填充c这个数组
    int x[a.size()],y[b.size()];
    memset(x,0,sizeof x);//就是初始化
    memset(y,0,sizeof y);//初始化*3
    for(unsigned int i=0;i<a.size();i++)
        x[a.size()-1-i]=a[i]-'0';//我们要先把他首尾转过来(因为我们都是从第一个数字的个位数开始算的)
    for(unsigned int i=0;i<b.size();i++)
        y[b.size()-i-1]=b[i]-'0';//转过来的同时还要把字符串变成数字
    for(unsigned int i=0;i<a.size();i++){
        for(unsigned j=0;j<b.size();j++){
            c[i+j]+=x[i]*y[j];//先乘,后面再进位
        }
    }
    for(unsigned int i=0;i<a.size()+b.size();i++){//进位
        if(c[i]>=10){
            c[i+1]+=c[i]/10;
            c[i]%=10;
        }
    }
    string ci;
    bool p=1;
    for(int i=a.size()+b.size()-1;i>=0;i--){//把前缀零给消掉并且把数字存起来
        if(c[i]==0&&p) continue;
        else{
            p=0;
            ci+=c[i]+'0';
        }
    }return ci;//返还回去一个字符串
}//*/

上面的代码里面就有每步的解析了(详细吧દ ᵕ̈ ૩)

然后我们就要面对高精除了,我们先看看数字,判断出来被除数会很大,而除数不会很大,所以我们只要导进去一个字符串,一个数字就行了(还没思考要是两个都是字符串会怎样),除法就是从头到尾都要除掉那个除数,剩下来的余数也要存起来,下次除接着用

再次献上代码~

string hdiv(string a,int b){//高精除法
    int x[50005];
    int y[50005];
    memset(x,0,sizeof(x));//初始化*1
    memset(y,0,sizeof(y));//初始化*2
    for(unsigned int i=0;i<a.size();i++){//转换
        x[i+1]=a[i]-'0';
    }
    int yu=0;//余数
    for(unsigned int i=1;i<=a.size();i++){//开除
        y[i]=(yu*10+x[i])/b;
        yu=(yu*10+x[i])%b;//余数下次还要再算进去
    }
    int kk=1;//用来删前置0的
    while(y[kk]==0&&kk<a.size()) kk++;
    string aa;
    for(unsigned int i=kk;i<=a.size();i++){//转换
        aa+=y[i]+'0';
    }return aa;//返还一个字符串儿
}

到这里我们就把这题的大纲和关键难点给弄出来了。剩下的就是写一些小点了,比如说数字转字符串,比大小(前面的贪心没忘了吧,这到底还是一道贪心题)

接下来献上完整的代码,真的好长啊,本蒟蒻第一次写那么长的解题代码

#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
string hmul(string a,string  b){//高精乘法
    int c[500005];
    memset(c,0,sizeof c);
    int x[a.size()],y[b.size()];
    memset(x,0,sizeof x);//初始化*1
    memset(y,0,sizeof y);//初始化*2
    for(unsigned int i=0;i<a.size();i++)
        x[a.size()-1-i]=a[i]-'0';
    for(unsigned int i=0;i<b.size();i++)
        y[b.size()-i-1]=b[i]-'0';
    for(unsigned int i=0;i<a.size();i++){
        for(unsigned j=0;j<b.size();j++){
            c[i+j]+=x[i]*y[j];
        }
    }
    for(unsigned int i=0;i<a.size()+b.size();i++){
        if(c[i]>=10){
            c[i+1]+=c[i]/10;
            c[i]%=10;
        }
    }
    string ci;
    bool p=1;
    for(int i=a.size()+b.size()-1;i>=0;i--){
        if(c[i]==0&&p) continue;
        else{
            p=0;
            ci+=c[i]+'0';
        }
    }return ci;
}//*/
string hdiv(string a,int b){//高精除法
    int x[50005];
    int y[50005];
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    for(unsigned int i=0;i<a.size();i++){//转换
        x[i+1]=a[i]-'0';
    }
    int yu=0;
    for(unsigned int i=1;i<=a.size();i++){//开除
        y[i]=(yu*10+x[i])/b;
        yu=(yu*10+x[i])%b;//余数下次还要再算进去
    }
    int kk=1;//用来删前置0的
    while(y[kk]==0&&kk<a.size()) kk++;
    string aa;
    for(unsigned int i=kk;i<=a.size();i++){
        aa+=y[i]+'0';
    }return aa;
}
string smx(string a,string b){//比大小
    if(a.size()!=b.size()) return a.size()>b.size()?a:b;//位数多的肯定更大点
    return a>b?a:b;
}
string tur_str(int num){//数字转字符串
    string str;
    while(num){
        str+=num%10+'0';
        num/=10;
    }
    reverse(str.begin(),str.end());//我真懒,真的,还特地去网上搜有没有一键倒置的函数
    return str;//还真让我找到了(偷偷乐
}
int n;
struct aa{
    int l,r;
}a[100005];
bool cmp(aa a,aa b){
    return (a.l*a.r)<(b.l*b.r);
}
int main(){
    cin>>n;
    cin>>a[0].l>>a[0].r;
    for(int i=1;i<=n;i++) cin>>a[i].l>>a[i].r;
    sort(a+1,a+n+1,cmp);
    string ans="0";//初始留个0
    string xx=tur_str(a[0].l);
    for(int i=1;i<=n;i++){
        ans=smx(ans,hdiv(xx,a[i].r));//相当于非高精的ans=max(mamx,xx/a[i].r);
        //cout<<"ans=="<<ans<<endl;//当时答案错误找错误时弄得
        xx=hmul(xx,tur_str(a[i].l));//每次都乘左手的数字
    }cout<<ans<<endl;
    return 0;
}//这代码真的好~长~啊!

(为了写着一题,我在csdn里面呆了好久,去看高精乘除和一些函数,真的学了好多东西,感觉是一道不错,挺综合的题鄙见

终于完成这题了,不知道大家看懂了没有σ( ᑒ ) ​

债见!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值