luoguP1080[NOIP2012提高组]国王游戏

话说要不是标签我都看不出来是个贪心。。

证明一下贪心

X X X的左手为 a 1 a_1 a1,右手为 b 1 b_1 b1

Y Y Y的左手为 a 2 a_2 a2,右手为 b 2 b_2 b2

假设X位于Y的前边

位于 X X X前的大臣左手的乘积和 S 1 S_1 S1位于 X X X前的大臣的右手的数为 M 1 M_1 M1
a 1 a_1 a1 b 1 b_1 b1
这一段 ( a 1 ( a_1 (a1 a 2 ) a_2) a2)的左手乘积 S 2 S_2 S2 Y Y Y前面大臣的右手的数 M 2 M_2 M2
a 2 a_2 a2 b 2 b_2 b2

此时:

X X X所得的金币: S 1 b 1 \frac{S_1}{b_1} b1S1

Y Y Y所得的金币: S 1 a 1 S 2 b 2 \frac{S_1 a_1 S_2}{b_2} b2S1a1S2

则最小金币数为 m a x ( S 1 b 1 , S 1 a 1 S 2 b 2 ) max( \frac{S_1}{b_1},\frac{S_1 a_1 S_2}{b_2}) max(b1S1,b2S1a1S2)

将X和Y的位置调换

位于 Y Y Y前的大臣的左手乘积和 S 1 S_1 S1位于 Y Y Y前的大臣的右手乘积和 M 1 M_1 M1
a 2 a_2 a2 b 2 b_2 b2
这一段的左手乘积 S 2 S_2 S2 X X X前面大臣的右手的数 M 2 M_2 M2
a 1 a_1 a1 b 2 b_2 b2

此时:

Y Y Y所得的金币: S 1 b 2 \frac{S_1}{b_2} b2S1
X X X所得的金币: S 1 a 2 S 2 b 1 \frac{S_1 a_2 S_2}{b_1} b1S1a2S2

则最小金币数为 m a x ( S 1 b 2 , S 1 a 2 S 2 b 1 ) max( \frac{S_1}{b_2},\frac{S_1 a_2 S_2}{b_1}) max(b2S1,b1S1a2S2)

假设X在前优于Y在前


①: m a x ( S 1 b 1 , S 1 a 1 S 2 b 2 max( \frac{S_1}{b_1},\frac{S_1a_1 S_2 }{b_2} max(b1S1,b2S1a1S2 < < < m a x ( S 1 b 2 , S 1 a 2 S 2 b 1 max( \frac{S_1}{b_2} , \frac{S_1 a_2 S_2}{b_1} max(b2S1,b1S1a2S2恒成立
因为②: S 1 b 1 < = S 1 a 2 S 2 b 1 \frac{S_1}{b_1}<= \frac{S_1a_2S_2}{b_1} b1S1<=b1S1a2S2 恒成立
且 ③ S 1 b 2 < = S 1 a 1 S 2 b 2 \frac{S_1}{b_2}<= \frac{S_1a_1 S_2}{b_2} b2S1<=b2S1a1S2恒成立

若要在②③基础上满足上述条件①则
首先要满足 S 1 a 1 S 2 b 2 < \frac{S_1a_1S_2}{b_2} < b2S1a1S2< S 1 a 2 S 2 b 1 \frac{S_1a_2S_2}{b_1} b1S1a2S2恒成立
a 1 b 1 < a 2 b 2 a_1 b_1 < a_2 b_2 a1b1<a2b2

粗略说一下

在②③基础上,左边 m a x max max S 1 a 1 S 2 b 2 \frac{S_1 a_1 S_2} {b_2} b2S1a1S2,右边取 S 1 a 2 S 2 b 1 \frac{S_1a_2S_2} {b_1} b1S1a2S2才能使①成立
综上
我们需按照左右手乘积的排列大臣
补充
不考虑向下取整是因为:不考虑向下取整的最优解在向下取整的条件下不会比其余的解更劣

高精度

100%的数据范围要求使用高精乘除(不用luogu提交只有60pts)

Code

#include<bits/stdc++.h>
#define ll long long
#define maxn 10005
using namespace std;
int n;
int lena=1,lens=1,lenm=1;
int sum[maxn]={0,1},minn[maxn]={0,1},ans[maxn];
//sum用来存储每一位大臣前边大臣的左手数字乘积
//ans存储每个大臣对应的sum值除以右手的值 
//minn存储最小的最大值 (即答案..... 
struct node{
    ll z,y;
}e[maxn]; 
bool cmp(node x,node y){
        return x.z*x.y<y.z*y.y;
}
void cheng(ll x){
    int num=0;
    for(int i=1;i<=lens;i++){
        sum[i]*=x;
    }
    for(int i=1;i<=lens;i++){
        num+=sum[i];
        sum[i]=num%10;
        num/=10;
    }
    while(num){
        lens++;
        sum[lens]=num%10;
        num/=10;
    }
}
void chu(ll x){
    memset(ans,0,sizeof(ans));
    lena=lens;
    int num=0;
    for(int i=lena;i>=1;i--){
        num*=10;
        num+=sum[i];
        if(num>=x){
            ans[i]=num/x;
            num%=x;
        }
    }
    while(ans[lena]==0&&lena>1){//除去前导零 
        lena--;
    }
}
void maxx(){
    if(lena>lenm){
        for(int i=1;i<=lena;i++){
            minn[i]=ans[i];
        }
        lenm=lena;
    }
    else if(lena==lenm){
        for(int i=lena;i>=1;i--){
            if(minn[i]<ans[i]){
                for(int j=1;j<=lena;j++){
                    minn[j]=ans[j];    
                }
                break;
            }
        }
    }
}
int main(){
    scanf("%d",&n);
    scanf("%d%d",&e[0].z,&e[0].y);
    sum[1]=1;//初始为1才能存进数去 
    for(int i=1;i<=n;i++){
        scanf("%d%d",&e[i].z,&e[i].y);
    }
    sort(e+1,e+n+1,cmp);
    for(int i=1;i<=n;i++){
        cheng(e[i-1].z);//乘 
        chu(e[i].y);//除 
        maxx();//取最大值 
    }
    for(int i=lenm;i>=1;i--){
        cout<<minn[i];
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值