首页 > 试题广场 >

绝命沙虫

[编程题]绝命沙虫
  • 热度指数:1065 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
风暴来,黄沙起,绝命沙虫剧毒见。

初始时你的手上有 N 元 RMB。

交易规则如下:

  • 你通过充值 a 元可以获得 红点和 绿点,其中 M 是充值返点倍率。

  • 你每次充值的数量必须是正整数,也就是说不能为零或负数。

  • 你通过出售 b 红点可以获得 元 RMB 和 点消费经验。 

  • 你通过出售 c 绿点可以获得 点消费经验。

给定 N,M,请回答你最后获得的消费经验是多少。

注意:任何时刻只要你手上有 RMB 或者有红点/绿点你都依次使用并且用光,你不会手上有而不用

输入描述:
全文第一行输入一个整数 ,表示数据组数。

第一行输入两个数 ,分别表示初始拥有的 RMB 数量和充值返点倍率。

数据保证 N 是正整数,M 一定是一位小数。


输出描述:
每行输出一个整数,你最后获得的消费经验是多少。
示例1

输入

2
10 1.5
160 2.0

输出

270
5760

说明

对于样例 #1,充入 10 RMB,获得 10\times100=1000 红点和 10\times100\times(1.5-1)=500 绿点。

花掉它们,获得 \dfrac{1000}{10}+\dfrac{500}{10}=150 消费经验和 \dfrac{1000}{200}=5 RMB。

充入 5 RMB,获得 500 红点和 250 绿点;花掉它们,获得 50+25=75 消费经验和 2 RMB。

充入 2 RMB,获得 200 红点和 100 绿点;花掉它们,获得 20+10=30 消费经验和 1 RMB。

充入 1 RMB,获得 100 红点和 50 绿点;花掉它们,获得 10+5=15 消费经验。

结算(最终获得):150+75+30+15=270 消费经验。

对于样例 #2,充入 160 RMB,获得 16000 红点和 10000 绿点;转换成 80 RMB 和 2600 消费经验;

充入 80 RMB,获得 8000 红点和 8000 绿点;转换成 40 RMB 和 1600 消费经验;

充入 40 RMB,获得 4000 红点和 4000 绿点;转换成 20 RMB 和 800 消费经验;

\cdots

充入 1 RMB,获得 100 红点和 100 绿点;转换成 20 消费经验。

结算(最终获得):2600+1600+800+400+200+100+40+20=5760 消费经验。

卡精度怎么办?有没有什么无脑的操作呢?有的兄弟,把卡掉的那一点精度加上就行了。令 eps(伊普西隆,非常小的一个正数) = 1e-9,强制类型转换时给m加上,这样子如果运算时出现x.99999...的数字时,加上eps后就会把精度向上补齐,再进行强转就ok了。当然,如果运算时出现x.0000...1的数,加上eps强转后也不会影响结果。

#include <iostream>
#include <algorithm>

using namespace std;
using ll = long long;
const double eps = 1e-9;

void solve() {
    ll n; double m; cin >> n >> m;
    ll r = 0, g = 0, e = 0;
    while(n) {
        r += n * 100;
        g += min(10000LL, ll(n * 100 * (m - 1 + eps)));
        n = 0;
        n += r / 200;
        e += r / 10;
        e += g / 10;
        r = 0, g = 0;
    }
    cout << e << endl;
}

int main() {
    int _; cin >> _;
    while(_--) {
        solve();
    }

    return 0;
}
发表于 2025-11-07 16:20:35 回复(1)
#include <stdio.h>
typedef struct{
    int exp;
} sum;
typedef struct{
    int Red;
    int Green;
}Points;
#define ll long long
int min (int a, int b);
int PointPutchar(int rmb, Points* p, int M_int);
void EXP_sum (Points p, sum* exp);

int main() {
    int T;
    scanf("%d", &T);
    while (T-- > 0){
        int N;
        float M;
        Points RedandGreen;
        sum exp = {0};
        scanf("%d %f", &N, &M);//读入充值的N元RMB与充值返点倍率M
        int M_int = (int)(M * 10);//转为整型int,1.5->15
        int useRMB = N;
        while (useRMB > 0){
            useRMB = PointPutchar(useRMB, &RedandGreen, M_int);
            EXP_sum (RedandGreen, &exp);
        }
        printf("%d\n", exp.exp);
    }
    return 0;
}
int min (int a,  int b){
   return a > b?b : a;
}
int PointPutchar(int rmb, Points* p, int M_int){
    p->Red = rmb*100;
    float base_green = rmb * 100 * (M_int - 10);//(15 - 10) = 5,还要再除个10
    p->Green = min(10000, base_green / 10);//转到下面来处理
    return p->Red / 200;
}
void EXP_sum (Points p, sum* exp){
    exp->exp += p.Green/10 + p.Red/10;
}

发表于 2025-11-07 19:14:28 回复(0)

看上去很简单,跟着算就行了,但如果真的无脑套公式的话你会发现,由于浮点数的精度丢失,会导致部分样例的答案会"莫名其妙"的出错,而在你察觉到这里的倍率应该要用double类型的时候会发现精度还是不够...所以这里只能展开公式里的括号,把计算放到更大的数量级里进行以避免(该死的)精度丢失...

#include <iostream>
using namespace std;
int main() {
    int t;
    cin >> t;
    for (int i = 0; i < t; i++) {
        int money;
        // 使用double类型,避免精度丢失
        double rate;
        int exp = 0;
        cin >> money >> rate;
        while (money > 0) {
            // 将公式展开,进一步避免精度丢失
            int green = money * (100 * rate - 100);
            if (green > 10000) {
                green = 10000;
            }
            exp +=  money * 10 + green / 10;
            money /= 2;
        }
        cout << exp << endl;
    }
}
发表于 2025-11-07 11:14:13 回复(0)