【洛谷 P1163】 银行贷款|浮点二分答案:金融计算
一、题目信息
题目链接:洛谷 P1163 银行贷款
题意简述:
已知贷款本金 $w_0$,每月固定还款 $w$,一共分 $m$ 个月还清。利息按月复利计算:每月剩余本金先乘 $(1+\text{月利率}x)$,再减去当月还款额。求月利率,输出时转为百分比,四舍五入保留 1 位小数。
数据范围:
- 题目保证月利率不超过 $300%$,即浮点二分右边界开到 10 完全够用。
样例推演(输入:1000 100 12):
本金 1000,每月还 100,分 12 个月还清,最终算出月利率约 $0.029$,乘以 100 输出 $2.9$。
二、核心解题思路:浮点二分答案
1. 题型判定:二分答案适用条件
本题是典型实数域二分(浮点二分),满足单调性:
- 若月利率 $x$ 可行(还完 $m$ 个月后剩余本金 $\ge 0$),则所有大于 $x$ 的利率一定更难还清,不可行;
- 若 $x$ 不可行(最后本金为负数,代表每月还款不足以覆盖利息,$m$ 个月还不完),则所有更小的利率都可行。
单调性完全成立,因此可以二分枚举月利率,通过 check() 函数验证当前利率是否合法,不断缩小区间逼近最优解。
2. 校验函数 check(x) 数学逻辑
设当前二分猜测的月利率为 $x$,模拟 $m$ 个月还款流程:
$$
s_{i} = s_{i-1} \times (1+x) - w
$$
- 初始 $s_0 = w_0$(贷款本金);
- 循环执行 $m$ 次月度复利 + 扣款;
- 最终判断:$s_m \ge 0$ 代表该利率下 $m$ 个月刚好 / 能还清,返回
true;否则返回false。
3. 浮点二分边界与精度控制
- 左边界 $l=0$:利率最低为 0,无利息;
- 右边界 $r=10$:对应 $1000%$,题目限制答案不超过 $300%$,区间足够;
- 循环终止条件:
r - l > 1e-5。我们最终要保留 1 位小数,二分精度开到 $10^{-5}$ 可以保证四舍五入无误差; - 精度重要性:如果精度设置过低,会出现输出结果偏差 0.1 的丢分情况。
三、完整 AC 代码
#include <bits/stdc++.h>
using namespace std;
double w0, w, m;
bool check(double x) {
double s = w0;
for (int i = 1; i <= m; i++) {
s = s * (1 + x) - w;
}
return s >= 0;
}
double find() {
double l = 0, r = 10;
while (r - l > 1e-5) {
double mid = (r + l) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
int main() {
cin >> w0 >> w >> m;
printf("%.1lf", find() * 100);
return 0;
}
四、代码逐段详细拆解
1. 全局变量定义
double w0, w, m;
w0:贷款原始本金;w:每月固定还款金额;m:分期总月数;- 全部使用
double存储,避免整数运算精度丢失。
2. 二分校验函数 check(double x)
bool check(double x) {
double s = w0;
for (int i = 1; i <= m; i++) {
s = s * (1 + x) - w;
}
return s >= 0;
}
- 初始化剩余本金
s等于贷款总额; - 循环模拟每一个月:先计算当月复利,再扣除当月还款;
- 循环结束后判断剩余本金是否大于等于 0:
true:当前利率x偏高,还款压力大,需要缩小利率区间;false:当前利率偏低,还款后还有结余,可以适当抬高利率。
3. 浮点二分核心函数 find()
double find() {
double l = 0, r = 10;
while (r - l > 1e-5) {
double mid = (r + l) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
- 初始化二分区间 $[0,10]$;
- 循环直到区间长度小于 $10^{-5}$,保证精度充足;
mid = (l + r) / 2:实数二分取区间中点;- 分支逻辑:
check(mid) == true:中点利率可行,最优解在左半区间,令r = mid;check(mid) == false:中点利率不可行,最优解在右半区间,令l = mid;
- 循环结束返回
l,即为精确到万分级的月利率小数。
4. main 主函数输入输出
int main() {
cin >> w0 >> w >> m;
printf("%.1lf", find() * 100);
return 0;
}
- 读取本金、月供、总月数;
- 二分得到小数利率,乘以 100 转为百分比;
printf("%.1lf")自动完成四舍五入,保留 1 位小数输出,完美匹配题目输出要求。
五、浮点二分关键知识点 & 踩坑提醒
1. 整数二分 vs 浮点二分区别
- 整数二分:循环条件
l <= r、mid = (l + r) >> 1,区间左右边界离散; - 浮点二分:循环条件为区间精度限制
r - l > eps,mid = (l + r) / 2,区间是连续实数,不能用位运算。
2. 精度 eps 怎么选?
本题要求输出保留 1 位小数,理论 eps 设 1e-3 即可,但稳妥起见统一用 1e-5/1e-6:
eps过小:循环次数变多,耗时轻微上涨;eps过大:输出四舍五入出错,比如正确答案 2.9,输出变成 2.8 或 3.0。
3. 区间边界设置技巧
题目说明答案不超过 $300%$,也就是 $x \le 3.0$,代码中 r = 10 是冗余安全边界,不影响程序效率。如果题目无上限,可通过循环倍增右边界找到合法区间。
4. 输出转换细节
代码最后 find() * 100 是核心:二分得到的 x 是小数利率(0.029 代表 2.9%),必须乘 100 转为百分比再打印。
六、同类拓展题型(浮点二分题库)
- P1902 刺杀大使:网格连通性 + 整数二分;
- P2440 木材加工:整数二分最大化最小值;
- P1182 数列分段 Section II:整数二分经典模板;
- P1570 KC 喝咖啡:实数二分 + 贪心校验。
七、全文总结
- 题型识别:求满足条件的最小 / 最大实数,优先浮点二分答案;
- 解题模板流程:确定二分区间 → 编写校验函数模拟过程 → 循环缩小区间至精度达标;
- 本题核心:利用金融复利公式模拟月度还款,单调性保证二分正确性;
- 应试要点:浮点二分注意精度
eps设置、百分比输出记得乘 100、使用printf格式化小数避免输出误差。
浙公网安备 33010602011771号