P1080 国王游戏

链接

P1080 国王游戏

思路

首先我们先思考一下当只有两个大臣的时候怎么做(当题目没有思路的时候,先考虑数据范围小的情况往往是有帮助的)。 如果国王左手上的数是 $a_0$,两个大臣左右手上的数是$a_1,b_1,a_2,b_2$;那么有两种排法: 第一个大臣排在前面,那么第一个大臣获得 $a_0 / b_1$ 奖赏,第二个获得 $a_0a_1 / b_2$ 奖赏,奖赏最多的大臣获得的奖赏就是 $max(a_0 / b_1, a_0a_1 / b_2)$。 同样,如果第二个大臣排在前面,答案就是$max(a_0 / b_1, a_0a_1 / b_2)$

比较 $ans_1=max(a_0 / b_1, a_0a_1 / b_2)$ 和 $ans_2=max(a_0 / b_1, a_0a_1 / b_2)$,可以写成 $ans_1=a_0 max(b_2,a_1\cdot b_1) / b_1\cdot b_2 ans_2=a_0 max(b_1,a_2\cdot b_2) / b_1\cdot b_2$ 可以想象,如果 $a_1\cdot b_1 ≤ a_2\cdot b_2,$那么 $ans_1 $肯定更小,因为这种情况下 $a_1\cdot b_1 ≤ a_2\cdot b_2 $而 $b_2 ≤ a_2\cdot b_2$,所以 $max(b_2,a_1\cdot b_1) ≤ a_2\cdot b_2 ≤ max(b_1,a_2\cdot b_2)$ 同理,如果$ a_1\cdot b_1 > a_2\cdot b_2$那么$ ans_2 $更小。 于是我们得到一个结论:只有两个人时,把$ a_i \cdot b_i $更小那个放到前面一定更优。

当有更多大臣的时候呢? 考虑最优解满足什么性质。如果我们交换最优解中两个相邻的大臣,那么他们前面的大臣得到的奖赏显然不受影响;而他们后面的大臣也不受影响。(想一想,为什么) 那么把这两个大臣单独拎出来,根据之前的结论,最优解中一定是 ab 较小的放到前面。 这样的话,我们得出结论:最优解中相邻两个大臣,前面的 ab 一定更小。否则可以交换相邻的大臣使得答案更小。

于是最优解肯定是按大臣的 $a\cdot b $排序后的结果。所以将大臣按$ a\cdot b $排好序,计算每个大臣的奖赏就可以了(这题代码难点在于高精度) 通过这道题我们看出一种贪心的分析方式,它尤其适用于“将若干个物品重新排列使得___最小/最大”的问题: 假设我们有一组解,考虑如何调整能使它更优(对于重新排列,一般是交换相邻物品); 最优解一定不能调整。

以来来自《noip杂题选讲 by rqy》

代码

主要是高精度(

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
using namespace std;
struct num {
    int len,s[10005];//len为数字位数,s数组为每一位上的数字
    num(int a=0) { //构造函数
        len=0;
        memset(s,0,sizeof(s));
        while(a) {
            len++;
            s[len]=a%10;
            a/=10;
        }
    }
    num operator * (const num &a) const {
        num c;
        int x;
        for(int i=1; i<=a.len; ++i) {
            x=0;
            for(int j=1; j<=len; ++j) {
                c.s[i+j-1]+=a.s[i]*s[j]+x;
                x=c.s[i+j-1]/10;
                c.s[i+j-1]%=10;
            }
            c.s[i+len]=x;
        }
        c.len=a.len+len;
        while(!c.s[c.len] && c.len!=1) c.len--;
        return c;
    }
    num operator / (const int &a) const { //重载整除运算(高精)
        num c;
        int x=0;
        c.len=len;
        for(int i=c.len; i>=1; --i) {
            x=x*10+s[i];
            c.s[i]=x/a;
            x%=a;
        }
        while(!c.s[c.len] && c.len!=1) c.len--;
        return c;
    }
    bool operator < (const num & x) const {//重载'<'(高精比较大小),max函数中要用
        if(len!=x.len) return len<x.len;
        for(int i=len; i>0; i--)
            if(s[i]!=x.s[i])
                return s[i]<x.s[i];
        return 0;
    }
};
struct node { //表示大臣的结构体
    int a,b,c;
} p[10005];
int cmp(node a,node b)
{
    return a.c<b.c;
}
int n;
int main() {
    cin>>n;
    for(int i=0; i<=n; ++i) {
        cin>>p[i].a>>p[i].b;
        p[i].c=p[i].a*p[i].b;
    }
    num sum(p[0].a),ans;
    sort(p+1,p+n+1,cmp);
    for(int i=1; i<=n; ++i) {
        ans=max(ans,sum/p[i].b);
        sum=sum*p[i].a;
    }
    for(int i=ans.len; i>=1; i--)
        cout<<ans.s[i];
    return 0;
}

转载于:https://www.cnblogs.com/pyyyyyy/p/11297511.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值