[luogu p1163] 银行贷款

传送门

银行贷款

题目描述

当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。

输入输出格式

输入格式

三个用空格隔开的正整数。

第一个整数表示贷款的原值,第二个整数表示每月支付的分期付款金额,第三个整数表示分期付款还清贷款所需的总月数。

输出格式

一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到\(0.1\%\)

输入输出样例

输入样例 #1

1000 100 12

输出样例 #1

2.9

分析

二分答案做法

此题我想到了两种思路,先从第一个开始说吧,既然是从二分题单来的,就先说说二分答案做法。

二分答案做法其实就是二分利率,都是套板子的事,问题是check函数怎么写。

首先我们来看一下利率公式:

\[\sum^k_{i=1}\dfrac{m}{(1 + p)^i} = n \]

其中原题中的三个值:

第一个整数表示贷款的原值,第二个整数表示每月支付的分期付款金额,第三个整数表示分期付款还清贷款所需的总月数。

就分别对应\(n, m, k\)

现在我们把这个式子变一下形。

\[\sum^k_{i=1}\dfrac{1}{(1 + p)^i} = \dfrac{n}{m} \]

等式左边其实就是公比为\(\dfrac{1}{1 + p}\)的等比数列求和。利用等比求和公式\(S_n = a_1\dfrac{1 - q ^ n}{1 - q}\)
可得:

\[\begin{aligned}\sum^k_{i=1}\dfrac{1}{(1 + p)^i} & = \dfrac{1}{1 + p} \times \dfrac{1 - (\dfrac{1}{1 + p}) ^ k}{1 - \dfrac{1}{1 + p}}\\& = \dfrac{1 - (\dfrac{1}{1 + p}) ^ k}{(1 + p) \times (1 - \dfrac{1}{1 + p})}\\ & = \dfrac{1 - (\dfrac{1}{1 + p}) ^ k}{p}\end{aligned} \]

此时原式可以转化为:

\[\dfrac{1 - (\dfrac{1}{1 + p}) ^ k}{p} = \dfrac{n}{m} \]

两边同\(\times p\)

\[1 - (\dfrac{1}{1 + p}) ^ k = \dfrac{n}{m} \times p \]

两边再同时\(- 1\),然后\(\times (-1)\)

\[(\dfrac{1}{1 + p}) ^ k = 1 - \dfrac{n}{m} \times p \]

这样我们就能写出check函数了:

bool check(double x) {
    return pow(1.0 / (1.0 + x), k) >= 1 - n / m * x;
}

小提示:pow函数需要#include <cmath>使用,pow(n, k)是n的k次方的意思。至于check函数为啥用大于等于而不是等于,是为了二分缩小范围服务的。不懂没关系看代码你就懂了。

check函数写完后就简单了,二分整体代码见一会的总体代码部分。

哦对了关于这个二分的边界问题我要说明一下,此题中存在\(p > 1\)的情况,所以右边界不能定为1。

相信有的同学想问了,蟹蟹这里也没有说利率的范围啊,右边界不好定怎么办?

就是因为这个,我思考了另一种方法:

倍增做法

用倍增写这道题也是不错的选择。check函数还是那个check函数,整体都差不多,只是main函数中的二分答案变成了倍增而已,没什么好说的。

但是这种方案不用定右边界,完美解决了这个问题。

就这样吧,上代码。

代码

二分答案做法

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-06-16 09:18:57 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-06-16 15:28:56
 */
//二分答案
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <cmath>

const double eps = 1e-10;

double n, m, k;
bool check(double x) {
    return pow(1.0 / (1.0 + x), k) >= 1 - n / m * x;
}

int main() {
    std :: cin >> n >> m >> k;
    double l = 0, r = 10086;
    while (r - l >= eps) {
        double mid = (l + r) / 2;//不能用位运算了啊啊啊啊啊啊啊啊暴风哭泣
        if (check(mid)) r = mid;
        else l = mid;
    }

    std :: cout << std :: fixed << std :: setprecision(1) << l * 100 << std :: endl;
    return 0;
}

倍增做法

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-06-16 09:18:57 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-06-16 15:27:24
 */
//倍增
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <cmath>

const double eps = 1e-10;
double n, m, k;

bool check(double x) {
    return pow(1.0 / (1.0 + x), k) >= 1 - n / m * x;
}

int main() {
    std :: cin >> n >> m >> k;
    double ans = 0;
    double add = 0.1;
    while (add >= eps) {
        if (!check(ans + add)) {//注意这里是!check哦,原因自己想想
            ans += add;
            add *= 2.0;
        }
        else
            add /= 2.0;
    }

    std :: cout << std :: fixed << std :: setprecision(1) << ans * 100 << std :: endl;
    return 0;
}

评测记录

评测记录

posted @ 2020-06-16 15:30  东北小蟹蟹  阅读(164)  评论(0编辑  收藏  举报