洛谷刷题小记

银行贷款 P1163

首先这一题的难点在于方程怎么列,对于我这种对于贷款一无所知的人,可谓是难上加难

我们设贷款原值为w0,也就是本金;每月支付的分期付款为w,还清贷款所需的总月数为m,月利率为k.

第一个月,剩下还没还完的贷款有(1+k)w0-w,第二个月有[(1+k)w0-w]w0-w,以此类推,到了第m个月还剩下:
$$
(1+k)^mw0-[1+1+k+(1+k)^2+…+(1+k)^{m-1}]
$$
因此我们就要判断m个月过后,剩下的贷款是否被还清,从而推出合适的月利率。

对于暴力法,我们可以从头遍历每一个月利率,分别代入到公式中,根据结果的正负性来判断月利率的正确与否。但是这一题结果的范围为0.0%~300.0%,每一次以0.1%的梯度遍历显然太慢了,这个时候我们就要引入二分法。

我们将范围中最小的利率和最大的利率分别当左右界限,即0和3(小数表示),然后每一次取中间的值代入公式,判断结果的正负性。如果结果大于0,就表示时间到了钱还没还完,即中点值代表的利率偏大,我们就要在小的里面找,因此就有r=mid;同理,如果结果小于0,表示还没到时间就还完了,中点值代表的利率偏小,就在大的里面找,l=mid。

值得注意的是,由于我们找到的值并不是最终的值(以0.1%为梯度不可能准确到恰好相邻的两端有一个结果为0,所以最终的取值为相邻结果绝对值的最小值)所以这里的二分是双闭区间,左右两端都能取到,因此是l=mid而不是l=mid+1.

理论结束,实践开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
using namespace std;
long double w0,w;
int m;
long double fun(double k){
int i;
long double res=w0;
for(i=1;i<=m;i++){
res=res+res*k-w;//利率计算公式
}
return res;
}
int main(){
cin>>w0>>w>>m;
double l=0,r=3;
while(r-l>=0.0001){//当左右边界差值过小视为退出条件
double mid=(l+r)/2;
if(fun(mid)>0){
r=mid;//利率问题的边界要算上
}
else{
l=mid;
}
}
printf("%.1lf",l*100);
return 0;
}