P1080 [NOIP2012 提高组] 国王游戏-高精度+贪心+排序

[NOIP2012 提高组] 国王游戏

题目描述

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

3 
1 1 
2 3 
7 4 
4 6

样例输出 #1

2

这个题目我一边听课一边写,调试了很久
总算过了,可能代码很乱,但是过程中遇到的细节、问题都在代码中注释出来了

#include <bits/stdc++.h>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%d",&x);
typedef long long ll;
const int N=1e5+10,mod=100003;
const int inf=0x3f3f3f3f;
struct E{
    ll u,v;
}s[N];
ll n,m,ans;
ll a,b;
ll w[N],tot;//fun1中得到的左手数乘积

ll sh[N],id;//fun2中得到的商
ll res[N],cnt;//结果,由sh得到
bool cmp(E a,E b){
    return a.v*a.u<b.v*b.u;
}
void fun1(ll x){
    ll t=0,tmp;
    for(int i=1;i<=tot;i++){
        tmp=(x*w[i]+t);
        w[i]=tmp%10;
        t=tmp/10;
    }
    while(t){
        w[++tot]=t%10;
        t/=10;
    }
}
void fun2(int x){
    ll t=0;
    id=0;
    if(x==1){//特殊情况:如果这个大臣右手数字为1,则用高精度得到的结果是错误的
    //应该直接把w赋值给sh 得到的结果恒等于
        for(int i=tot;i>=1;i--){
            sh[tot-i+1]=w[i];
        }
        id=tot;
    }else{
        int f=0;
         for(int i=tot;i>=1;i--){
            t=t*10+w[i];
            if(t>=x){
                sh[++id]=t/x;
                if(f==0) f=1;

                if(t%x==0){//特殊情况:注意0 比如100/10
                    int i_tmp=i-1;
                    while(w[i_tmp]==0&&i_tmp>=1){
                        sh[++id]=0;
                        i_tmp--;
                    }
                    i=i_tmp+1;
                }

                t=t%x;
            }else if(f==1){//注意这里:有可能某个位不能整除,需要后移
                sh[++id]=0;
            }
        }
        while(t>x){
            sh[++id]=t/x;
            t=t%x;
        }
    }
    if(id>cnt||(id==cnt&&res[1]<sh[1])){//是否更新答案
        for(int i=1;i<=id;i++){
            res[i]=sh[i];
        }
        cnt=id;
    }
}
int main(){
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++){
        sf(s[i].u)sf(s[i].v)
    }
    sort(s+1,s+n+1,cmp);
    while(a){//把a放进w(乘积数组)中
        w[++tot]=a%10;
        a/=10;
    }
    for(int i=1;i<=n;i++){
        if(i>1) fun1(s[i-1].u);//第一个大臣不用乘以它前一个人左手的数字了
        //即是国王左手的数字
        fun2(s[i].v);
    }

    if(cnt==0){//特殊情况:都不满足
        printf("0\n");
        return 0;
    }
    for(int i=1;i<=cnt;i++){
        printf("%d",res[i]);
    }
    return 0;
}

2023年9月17号

又刷到了这个题目,但是也调试了很久,需要注意:
1、国王的位置不变,因此排序时sort的下标从数组的第二个开始
2、除数数组在每次进行更新前需要全部初始化为0

详细思路见注释

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define sf(x) scanf("%d", &x);
#define de(x) cout << x << " ";
#define Pu puts("");
const int N = 2e4 + 9, mod = 1e9 + 7;
int n, m;
int cheng[N], chu[N], ans[N];
struct E {
    int l, r;
} e[N];
bool cmp(E a, E b) {
    return (a.l * a.r < b.l * b.r);
}
void times(int x) {  // 大数乘法
    int t = 0;
    for (int i = 1; i <= cheng[0]; i++) {
        t = cheng[i] * x + t;
        cheng[i] = t % 10;
        t /= 10;
    }
    int id = cheng[0];
    while (t) {
        cheng[++id] = t % 10;
        t /= 10;
    }
    cheng[0] = id;
}
void divition(int x) {            // 大数除法(向下取整)
    memset(chu, 0, sizeof(chu));  // 除数需要置为0
    int t = 0;
    for (int i = cheng[0]; i >= 1; i--) {
        t = t * 10 + cheng[i];
        chu[i] = t / x;
        if (chu[0] == 0 && chu[i] != 0) {
            chu[0] = i;  // 如果当前商为0,并且得到的商不为0
            // 此时i即为最后商的位数
        }
        t %= x;
    }
}
bool compare() {
    if (ans[0] == chu[0]) {
        for (int i = chu[0]; i >= 1; i--) {
            if (chu[i] > ans[i])
                return 1;
            if (chu[i] < ans[i]) {
                return 0;
            }
        }
    }
    if (ans[0] < chu[0])
        return 1;
    if (ans[0] > chu[0]) {
        return 0;
    }
    return -1;
}
void cp() {
    ans[0] = chu[0];
    for (int i = chu[0]; i >= 1; i--) {
        ans[i] = chu[i];
    }
}
int main() {
    cin >> n;
    n++;
    for (int i = 1; i <= n; i++) {
        cin >> e[i].l >> e[i].r;
    }
    sort(e + 2, e + n + 1, cmp);  // 注意国王的位置不变
    cheng[0] = 1;                 // 位数
    cheng[1] = 1;                 // 因为是乘法,所以用1乘以其他的数
    for (int i = 2; i <= n; i++) {
        times(e[i - 1].l);  // 乘以它前面人的左手数
        divition(e[i].r);   // 除以自己的右手数
        if (compare()) {    // 如果大于当前存储的最大值,则更换
            cp();
        }
    }
    for (int i = ans[0]; i >= 1; i--) {
        printf("%d", ans[i]);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值