ACM第十一周二分总结

神奇的二分

//x:待查找的值,Caculate():所要查找的函数,在这里单调递增 
  //需保证查找的值在区间范围内
double low=“区间下界”,high=“区间上界”,mid;
while(high - low > 1.0e-6)
{
	 mid = (high + low)/2;
    if(Caculate(mid)<x)
        low=mid;
    else
        high=mid;
}

对之前博客的反思:
发现自己总结的知识真正体现思考的内容较少,博客的作用没有充分利用好,自己一股脑写出来的内容,很繁琐而且逻辑性差,自己都不想看。对于解题的内容的博客,没有将知识内化于心,只是像是知识点的复习,题解的巩固,做题过程(如何做的题今后该如何解这类题)没有详细总结,这一点也需要注意,改进。

回顾本周:
D题意:每列找一个数,得到和为0的序列,有几种不同的方案。
之前有了解二分,但没有刷题,发现学过和会做相差甚远,这道题看到一点思路也没有,用二分也不知道从何而用。这道题目是给出固定的四列,每列找到一个数,如果选用暴力的方法,最终肯定会超时。所以,就想到为了降低时间复杂度可以选用二分法。怎么利用二分呢?是四个数字的相加,巧妙的将其分成两组,先将前两列数的所有组合结果求出存放在一个数组中,再进行排序,然后利用二分查找去有序的数组中查找后两列组合结果的相反数。

几天后:一开始做的时候没思路是看的别人思考的代码,现在我自己用自己的想法重新做,提交了十几遍差不多快疯了还好最后AC。主要是对二分边界问题的不理解,实践的细节问题。

//修改后:
void find(int v)
{
    int l=0;
    int rr=r-1;
    int m=0;
     //cout << i++<<' '<<l<<' '<<rr<< endl;
    while(l<=rr)
    {
        m=(rr+l)/2;
        if(s1[m]>v)
            rr=m-1;
        else l=m+1;
    }
    //cout << i++<<' '<<l<<' '<<rr<< endl;
    while(s1[rr]==v&&rr>=0)//保证多种情况
    {
        ans++;
        rr--;
    }
}
//修改前:
void find(int v)
{
    int l=0;
    int rr=r-1;
    int m=0;
    while(l<rr)
    {
        m=(rr+l)/2;
        if(s1[m]<v)
            l=m+1;
        else rr=m;
    }
    while(s1[l]==v&&l<r)//保证多种情况
    {
        ans++;
        l++;
    }
}

K题意: 二分枚举相邻两牛的间距,判断大于等于此间距下能否放进所有的牛有n个牛栏,选m个放进牛,相当于一条线段上有 n 个点,选取 m 个点,使得相邻点之间的最小距离值最大。该怎么做呢?没思路看了一下别人的解题,好像是用到了一个半的二分。这道题该怎么运用二分。首先是求解最小距离最大值,我们要找的最小距离就是二分中的mid,那么显然它的初始是最大的距离mid=(l+r)/2;然后的工作是去判断mid是不是要求的最大的最小距离,if(cnt>=c) return 1;//c要放入的牛的个数,cnt记录能放入的牛的个数,该条件是该题特有的终止条件,我们在判断牛槽之间距离来确定是否放入小牛时,如果能放入的牛数量达到了要求的数量c就是我们从大到小第一次找到了那个最小距离,那个就是最大的。

E题意:给N个数,两两相减有m=C(N,2)种结果,找出(m/2)-th小的差。
解题过程:当知道题目是让N个数随机做差时找到,差值的中位数,首先C(N,2)个数遍历下来一定超时,这时候就需要考虑如何解决了,我现在也不很清楚在众多算法中怎么想到的二分,先分析一下如何运用二分的吧(这是前几天的想法,我现在觉得就是要用二分,例题),首先进行排序后,题中的差值由小到大,就能想到需要先确定差值再判断是否是中位数了,因此就需要用两个二分(有丶草率,接下来详细分析一下)。
思路:两次二分,第一次二分找那个差数,第二次算出左边有多少比他小的数可以使它们的差小于第一次二分时的数。这个题与上一个有点类似,发现上一个题cnt好像并不是特有的。很多博主解释是双重二分,我有点模糊,但这里确实是两个二分思想,在mid初值的情况下找这cn2个差值中大于mid的个数,然后判断是不是这个个数为中位数,需要注意的是这个判断还是一个二分while(l<r) {,,,if(num>=m) r=mid;else l=mid+1;,,,,}。(整体思路是假设mid是中位数,判断这个mid以后的个数是不是总个数的一半)

while(l<r)
        {
            mid=(l+r)>>1;
            j=1;
            num=0;
            for(i=2; i<=n; i++)
           {
                while(a[i]-a[j]>mid)
                    j++;
                num+=(i-j);//为什么直接下标相减相加就可以计算小于mid的个数num嘞
}//模拟一下,因为是二项式Cn2个数量,想一想cn2就明了了
            if(num>=m)
                r=mid;
            else l=mid+1;
        }

M题一不小心找到了规律没有用二分,但后来还是学习了二分这种方法来解题。核心公式:midd>=wk。这道题需要特别强调边界问题,又一次超出认知。

while(l<=r)
        {
            mid=(l+r)/2;
            if(mid*d>=w*k)//判断mid时间能否完成检查
            {
                r=mid-1;
                ans=mid;
            }
            else
            {
                l=mid+1;
            }
        }

非二分的方法也放这吧~

#include <iostream>
 
using namespace std;
 
int main()
{
    int T;
    int n,m,k;
    cin >> T;
    while(T --)
    {
        cin >> n >> k >> m;
        int  ans ;
        if(m >= n) ans = k;
        else if(n * k % m == 0)
            ans = n * k / m;
        else ans = n * k / m + 1;
        cout << ans << endl;
    }
    return 0;
}

关于二分做的题太少啦,总感觉有点明白但又不充分,目前为止做的二分这几道题能感觉出来他们的思想都是套路,但是最关键的地方,也是我们需要加强学习的是,二分思想和题目要求的对接,从题干中找到二分的左右边界,以及mid的值的含义以及mid与其他变量的关系式,有时候二分只是解题的一部分,最终的结果并不一定是题解,像其中双重二分的题目。感觉不只有这些,二分还会有很多很多题型会有更深刻的解释,继续加油。

经常反思总结,可以督促自己进步,尽管之前做的不好但也不能因此麻木忘记去变得更好,勇于跳出舒适圈向前看。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值