洛谷P1080 [NOIP2012 提高组] 国王游戏(贪心、高精度经典练习题)

1 篇文章 0 订阅
1 篇文章 0 订阅

题目

题目描述

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

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

输入格式

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

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

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

输出格式

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

输入输出样例

输入 #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% 的数据,保证答案不超过 10^9;

对于 100% 的数据,有 1≤n≤1,000,0<a,b<10000。

NOIP 2012 提高组 第一天 第二题

思路

这一题需要用到贪心,该怎么贪呢?

本题的贪心只要几行代码,却是本题中最难的部分。

开始推导:

假设有两位大臣,分别是i和j(i在前,j在后),每个人的左手和右手分别是l数组和r数组。再假设:

l[1]*l[2]*l[3]*l[4]*......l[i-1]=p

那么:

第i位大臣手上的金币数就是:

第j位大臣手上的金币数就是:

如果我们用邻项交换法,将i和j交换一下位置(i在前,j在后)的话:

对于第i位大臣,他手上的金币数就是:

对于第j位大臣,他手上的金币数就是:

结合图再来看看吧!

那如果我想证明i在j前面比j在i前面更好(即i在j前面金币数的最大值比j在i前面金币数的最大值更小),那么应该满足的条件是:

我们可以发现这四个分数上都有p这个分子。我们可以将p约掉,就变成了这样:

我们想让这个不等式一定成立,则应该满足的条件是

在如果后面)根据交叉相乘法,得到当时,i在j前面比j在i前面更好。

于是我们只需要将每一个大臣(包括国王)的左手乘右手从小到大排一个序就行了。

高精度

本体的高精度还是~很~简~单~的o(* ̄︶ ̄*)o,只要两个,一个是高精度乘高精度的乘法,还有一个是高精度除以低精度的除法。

代码(请勿抄袭!)

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node
{
    int l,r;
}aa[1001];
string sum="1",ans[1001];
int n;
int a[10001],b[10001],c[10001];
bool cmp(node x,node y)      //排序
{
    return x.l*x.r<y.l*y.r;
}
string f(int x)     //将一个数转换成字符串类型
{
    string s="";
    while(x>0)
    {
        s+=(x%10)+'0';
        x/=10;
    }
    reverse(s.begin(),s.end());
    return s;
}
string cheng(string s,string s1)    //高精度乘高精度
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    reverse(s.begin(),s.end());
    reverse(s1.begin(),s1.end());
    for(int i=0;i<s.size();i++)a[i]=s[i]-'0';
    for(int i=0;i<s1.size();i++)b[i]=s1[i]-'0';
    for(int i=0;i<s.size();i++)              //模拟乘法竖式
    {
        for(int j=0;j<s1.size();j++)
        {
            c[i+j]+=a[i]*b[j];
            c[i+j+1]+=c[i+j]/10;     //进位
            c[i+j]%=10;
        }
    }
    int len=s.size()+s1.size()-1;
    while(c[len]==0 && len>0)len--;    //除去前导0
    string s2="";
    for(int i=0;i<=len;i++)
    {
        s2+=c[i]+'0';
    }
    reverse(s2.begin(),s2.end());
    return s2;
}
string chu(string s,int b)       //高精度除以低精度
{
    memset(a,0,sizeof(a));
    memset(c,0,sizeof(c));
    for(int i=0;i<s.size();i++)a[i]=s[i]-'0';
    int m=0;
    for(int i=0;i<s.size();i++)     //模拟除法竖式
    {
        c[i]=(m*10+a[i])/b;
        m=(m*10+a[i])%b;
    }
    int len=0;
    while(c[len]==0 && len<s.size()-1)len++;
    string s2="";
    for(int i=len;i<s.size();i++)
    {
        s2+=c[i]+'0';
    }
    return s2;
}
signed main()
{
    cin>>n;
    int a1,b1;
    cin>>a1>>b1;
    aa[1].l=a1,aa[1].r=b1;
    for(int i=2;i<=n+1;i++)
    {
        cin>>aa[i].l>>aa[i].r;
    }
    sort(aa+2,aa+2+n,cmp);   //排序(为什么要加二?那是因为还要算上国王)
    for(int i=1;i<=n+1;i++)
    {
        string l=f(aa[i].l);
        sum=cheng(sum,l);
        string s=sum;
        s=chu(s,aa[i].l);         //将每一个人的金币数放到ans数组里
        s=chu(s,aa[i].r);
        ans[i]=s;
    }
    string anss="1";
    for(int i=2;i<=n+1;i++)
    {
        if(ans[i].size()>anss.size() || (ans[i].size()==anss.size() && ans[i]>anss))
        {
            anss=ans[i];      //找出金币数最大的哪一个
        }
    } 
    cout<<anss;   //输出
    return 0;
}

看我这么辛苦写的一篇题解,中途电脑了出故障,重写了一边,能不能点个赞发个评论再走呀~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值