二分刷题记录一

本文记录了学习二分法的过程,通过分析P1024一元三次方程求解、P2249查找问题和P1873砍树问题的解题思路,探讨了如何运用二分法解决不同类型的问题。在方程求解中,除了暴力枚举和盛金公式,还展示了如何通过小区间二分来优化。在查找问题中,利用递增顺序直接进行二分查找。在砍树问题中,通过二分寻找合适的木材长度。此外,还提及了银行月利率的二分逼近计算方法。总结中强调了二分法的灵活性及其与其他算法的结合应用。
摘要由CSDN通过智能技术生成

前言

学习了几天的排序开始了二分的学习,二分的思想就是将一段数据不断从中间分割,在分出的两份数据中只留下需要的数据在的那一部分,对数据范围不断缩小,最后得到需要的数据。最常见的就是利用二分思想去解二元一次方程组,但是当方程组的次数增加的时候又该如何用二分的思想求解呢?

一、P1024 [NOIP2001 提高组] 一元三次方程求解

在这里插入图片描述
初步解题思路
1.最简单的思路无非是用暴力枚举,数据范围不是很大,运行不会超时
2.使用盛金公式直接进行求根
3.利用二分,对每个小区间进行求根

1.暴力枚举

#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std;
int main()
{
	double a, b, c, d;
	cin >> a >> b >> c >> d;
	for (double i = -100; i <= 100; i+=0.001)
	{
		double j = i + 0.001;
		double y1 = a * pow(i, 3) + b * pow(i, 2) + c * i + d;
		double y2 = a * pow(j, 3) + b * pow(j, 2) + c * j + d;
		if (y1*y2<=0) {//直接判断根
			cout << setiosflags(ios::fixed) << setprecision(2) << (i + j) / 2 << " ";
		}
	}
}

2. 盛金公式

这个没有什么技巧,全靠数学公式的运用

#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std;
int main()
{
	double a, b, c, d;
	cin >> a >> b >> c >> d;
	double A = pow(b, 2) - 3 * a * c;
	double B = b * c - 9 * a * d;
	double C = pow(c, 2) - 3 * a * d;
	double Q = acos((2 * A * b - 3 * a * B) /( 2 * sqrt(pow(A, 3))));
	double x1 = (-b - 2 * sqrt(A) * cos(Q / 3)) / (3 * a);
	double x2 = (-b + sqrt(A) * (cos(Q / 3) - sqrt(3) * sin(Q / 3))) / (3 * a);
	double x3 = (-b + sqrt(A) * (cos(Q / 3) + sqrt(3) * sin(Q / 3))) / (3 * a);
	cout << fixed << setprecision(2) << x1 << " ";
	cout << fixed << setprecision(2) << x2 << " ";
	cout << fixed << setprecision(2) << x3 << " ";
}

3.小区间二分

#include<iostream>
#include<iomanip>
using namespace std;
double a, b, c, d;
double func(double x)
{
    return a * x * x * x + b * x * x + c * x + d;
}
int main()
{
    cin >> a >> b >> c >> d;
    double l, r, m, x1, x2;
    int s = 0;//留一个判断根个数的标记,用于减少时间
    for (int i = -100; i < 100; i++)
    {
        l = i;
        r = i + 1;
        x1 = func(l);
        x2 = func(r);
        if (!x1)
        {
            cout << fixed << setprecision(2) << l << " ";
            s++;
        }      //判断左端点,是零点直接输出;不能判断右端点,会重复。
        if (x1 * x2 < 0)                             //区间内有根。
        {
            while (r - l >= 0.001)                     //二分控制精度。
            {
                m = (l + r) / 2;  //计算中间值
                if (func(m) * func(r) <= 0)//判断根所在的区间后只留下需要的区间,减小范围
                    l = m;
                else
                    r = m;  
            }
            cout << fixed << setprecision(2) << r << " ";
            //输出右端点。
            s++;
        }
        if (s == 3)
            break;
        //找到三个就退出,一元三次方程最多三个根
    }
    return 0;
}

二、P2249 【深基13.例1】查找

在这里插入图片描述

初步解题思路
仔细读题,已经告诉我们这里输入的所有的数都是递增的,所以不用排序直接进行查找就好了。利用二分的方法直接对每个数进行查找

#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
    int n, m;
    cin >> n >> m;
    int a[n];
    for (int i = 0; i < n; i++)
        cin >> a[i];
    while (m--)
    {
        int s;
        cin >> s;
        int l = 0, r = n - 1;
            while (l < r) {
                int mid = l + r>>1;//这里指取对l+r的一半而且右移一位,即向右取整
                if (a[mid] >= s) r = mid;
                else l = mid+1;
            }
            if (a[l] == s)cout << l + 1<<" ";
            else cout << "-1 ";
    }
    return 0;
}

三.P1873 砍树

在这里插入图片描述
初步解题思路
先找到最高的树,取它的一半作为标准,取能砍的一棵树的木材求和,这样得到的木材的总和与需要的木材的长度进行比较。如果大了就将设定的最小值(0)改变为现在中间值大一点的值,最高值不改变;如果小了就将设定的最大值改变为现在中间值小一点,最小值不变。这样重复操作,直到最后取得想要的结果。

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    long long N, M,h=0;
    cin >> N >> M;
    long long a[N];
    for (int i = 0; i < N; i++) {
        cin >> a[i];
        h = max(h, a[i]);
    }
    long long l = 0; 
    while (l <= h) {
        long long mid = (l + h)/2;
        long long sum = 0;
        for (int i = 0; i < N; i++)
            if(a[i]>mid)
            sum += a[i] - mid;
        if (sum < M)
            h = mid-1;
        else
            l = mid + 1;
    }
    cout << l - 1;
    return 0;
}

知识卡片

1.银行月利率计算方法

可以利用二分法进行逼近,具体实现过程看代码以及解释

double l = 0, r = 1000, mid = 0;
    while (l < r - 0.0001) {//注意这里需要的精度
        mid = (l + r) / 2;//缩短每一次的区间
        double w = a;//a是原来的本金
        for (int i = 0; i < c; i++)//c是还款时长
            w = w - b + w * (mid / 100);//w是每个月的本金,mid/100是月利率
        if (w > 0.0001)
            r = mid;
        else
            l = mid;
    }
    cout<<l<<endl;//这里需要精度为多少就保留多少

总结

今天对二分有了一定的了解,初步做了几个题,其实思想很简单,但是要能够灵活运用还需要一些练习。二分也是能和其他很多算法只是综合起来出题的一种思想,要能够对能运用它的题目做出迅速的反应。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以给你一些代码,可以让你使用Markdown语言记录你的刷题记录:``` # 刷题记录## 今日刷题- 题目 1: - 解答:- 题目 2: - 解答:## 昨日刷题- 题目 1: - 解答:- 题目 2: - 解答: ``` ### 回答2: 生成一个markdown语言的代码刷题记录可以通过以下步骤完成: 1. 创建一个markdown文件,命名为"刷题记录.md",并打开编辑器。 2. 在文件中添加一个标题,例如"刷题记录"。 3. 在下方添加一个表格,表格包含以下列:日期、题目名称、题目难度、代码链接、思路。 4. 为每一道题目添加一行记录,按照日期倒序排列。每行填写相应的信息,包括日期、题目名称、题目难度、代码链接和思路。 示例: | 日期 | 题目名称 | 题目难度 | 代码链接 | 思路 | | -------- | ------------ | ---------- | ----------- | -------- | | 2021/1/1 | 两数之和 | 简单 | [代码链接]() | 思路描述 | | 2021/1/2 | 反转链表 | 中等 | [代码链接]() | 思路描述 | | 2021/1/3 | 接雨水 | 困难 | [代码链接]() | 思路描述 | | ... | ... | ... | ... | ... | 5. 在代码链接处添加相应题目的代码链接,可以使用GitHub或Gist等代码托管平台。 6. 在思路处添加题目解法的思路描述,可以简要概括解法的关键步骤或思想。 完成以上步骤后,保存并关闭markdown文件。这样就生成了一个简单的代码刷题记录,方便查看和整理自己的刷题历程,以及回顾解题思路。 ### 回答3: 生成一个markdown语言的代码刷题记录可以按照以下步骤进行: 1. 首先创建一个markdown文件,可以以`.md`为扩展名,比如`leetcode.md`。 2. 在markdown文件中使用标题,表示题目的名称和难度。比如: ``` # [题目名称](题目链接) - 难度 ``` 3. 接下来,按照以下格式记录每道题目的代码和解题思路: ``` ## 题目描述 题目的具体描述和要求。 ## 解题思路 解题的思路和步骤。 ## 代码实现 使用适当的代码块标记,插入代码。 ```python # Python代码示例 解法代码 ``` ```cpp // C++代码示例 解法代码 ``` ## 复杂度分析 对解法进行时间复杂度和空间复杂度的分析。 ## 总结 对题目和解法进行总结。 ``` 4. 按照上述格式记录完每道题目的代码和解题思路后,保存markdown文件。 5. 在记录新的刷题内容时,可以按照上述格式在文件后面追加新的题目记录。 总结起来,生成一个markdown语言的代码刷题记录,需要创建一个markdown文件,并按照一定的格式记录每道题目的代码和解题思路。这样可以方便地查看每道题目的代码和解法,并进行总结和复习。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值