计蒜客题解——信息学1月CSP-J2模拟赛——B. 存款

解析计蒜客CSP-J2模拟赛B题,介绍如何通过二分查找解决存款问题,避免直接计算导致的数据溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

计蒜客信息学1月CSP-J2模拟赛B题,https://nanti.jisuanke.com/t/43039

本题难度不大,但是题目出得非常的巧。

题目

DD 现在养成了在银行存款的好习惯,DD 存款的方式是每年放入 N 元并将之前的所有本金和利息也一起投进银行。在每一年年终,DD 的存款都会增长 P%(下取整到最近整数)。在 Y 年之后,她想拥有最少 T 元,DD 现在想知道,每年投入的 N 元最少是多少能够在 Y 年后获得最少 T 元。

输入格式

第一行三个整数,分别表示 P, Y, T。

输出格式

共一行,输出 N 最少是多少。

样例输入

100 2 300

样例输出

50

数据范围

对于 30% 的数据,1 \leqslant Y \leqslant 10^{3}, 1 \leqslant T \leqslant 10^{3}

对于 60% 的数据,1 \leqslant Y \leqslant 10^{3}, 1 \leqslant T \leqslant 10^{6}

对于 100% 的数据,1 \leqslant Y \leqslant 10^{6}, 1 \leqslant T \leqslant 10^{9}, 1 \leqslant P \leqslant 100

题目分析

题目要求

题目的意思就是根据存款利率(P)、存款年份(Y)和最终金额(T),倒推出存款金额(N)。

本题注意几个细节:

1、每年的金额需要下取整到最近整数。

2、每年将本金和利息也一起投进银行。

数学推导

因此,我们可以推算出:

1、第一年:N\ast (1+P\%)

2、第二年:(N\ast (1+P\%)+N)\ast (1+P\%)=N\ast (1+P\%)^{2}+N\ast (1+P\%)

3、第三年:((N\ast (1+P\%)+N)\ast (1+P\%))\ast (1+P\%)\\=N\ast (1+P\%)^{3}+N\ast (1+P\%)^{2}+N\ast (1+P\%)^{1}

4、...

Y、第Y年:N\ast (1+P\%)^{Y}+N\ast (1+P\%)^{Y-1}+\cdots +N\ast (1+P\%)^{1}\\=N\ast((1+P\%)^{Y}+(1+P\%)^{Y-1}+\cdots +(1+P\%)^{1})

也就是说T=N\ast((1+P\%)^{Y}+(1+P\%)^{Y-1}+\cdots +(1+P\%)^{1})\\\therefore \\N=\frac{T}{(1+P\%)^{Y}+(1+P\%)^{Y-1}+\cdots +(1+P\%)^{1}}

问题归结到求出(1+P\%)^{Y}+(1+P\%)^{Y-1}+\cdots +(1+P\%)^{1}这个值即可。说干就干,也许我们就可以写出这样的一个代码。

题目坑点

1、计算利息的时候要使用浮点数,而题目提供的P是整数。

2、每年计算需要下取整。

直接计算代码(只能通过2组数据)

#include <bits/stdc++.h>
using namespace std;
int main(){
    double p;
    int y, t;
    cin >> p >> y >> t;
    p = 1+p/100.0;

    double ans = 0;
    for (int i=0; i<y; i++) {
        ans += p;
        p *= p;
    }
    ans = t/ans;
    cout << (int)ceil(ans) << endl;

    return 0;
}

别急,我们分析一下数据规模,注意1 \leqslant Y \leqslant 10^{6}, 1 \leqslant P \leqslant 100,我们来看一下这个数据(1+P\%)^{Y}+(1+P\%)^{Y-1}+\cdots +(1+P\%)^{1},当Y = 10^{6}, P=1的时候将多大。1.01^{1000000}+1.01^{999999}+\cdots +1.01 > 1.01^{1000000}=2.36*10^{4321}

我去,这是一个天文数据,远远超过了C++最大的是数据类型unsigned long long的大小,也就是说只要数据量变大,这个程序就爆了。可以尝试提交一下,果然只能通过两组测试数据。

二分查找

由于数据量实在太大,没办法直接计算。我们可以知道,N的值范围必然是1 \leqslant N \leqslant T-1,程序的思路变成假设一个N值,验算在这样条件下,经过Y年后,将得到的数据。

为了降低复杂度,我们可以使用过二分查找,这样本题就变成一个标准的二分查找模板题。

#include <bits/stdc++.h>
using namespace std;
int main(){
    double p;
    long long y, t;
    cin >> p >> y >> t;
    p = 1+p/100.0;

    //二分查找
    int left=1;
    int right=t;
    int mid;
    long long n;
    while (left<=right) {
        mid = (right+left)/2;

        //根据的钱,计算y年后
        n = 0;
        for (int i=0; i<y; i++) {
            n = (n+mid)*p;
            if (n>t) {
                break;
            }
        }
        if (n>t) {
            right = mid-1;
        } else if (n<t) {
            left = mid+1;
        } else {
            break;
        }
    }

    cout << mid << endl;

    return 0;
}

程序说明

1、对于坑点1,也就是利率计算问题。我们可以将P定义为double类型。

2、变量n的数据类型,不能使用int。在数据量大的时候,计算n很容易超过int数据范围,因此需要使用long long。

3、对于坑点2,也就是下取整问题。我们可以使用隐式数据转换来实现,也就是代码的

n = (n+mid)*p;

这个地方。当然也可以使用显示强制数据转换来实现。

4、在验证二分数据的时候,Line 21 ~ Lline 23,就是当计算出N> T的时候,就不需要继续验证下去了。

5、由于个人水平问题,这个代码目前只能通过9组测试数据,还有一组测试数据错误,我会进一步验证一下问题在哪里。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值