5月23日一周学习总结

本周重点学习了二分查找算法,应用于解决单调性问题,通过优化暴力遍历提高效率。同时,探讨了如何在实际题目中找到二分的切入点,例如在求解最短距离和素数问题上的应用。此外,还学习了深度优先搜索(DFS)和广度优先搜索(BFS),理解了递归在解决问题中的作用,特别是递归与动态规划的相似性。
摘要由CSDN通过智能技术生成

5月23日一周学习总结

二分问题

这周做题以二分问题为主,个人认为二分法比较适用于单调性问题,因为如果不满足这个条件,则问题会出现多个解,二分法同时也是对于暴力遍历的优化,因为二分的运算量是log级别的,在对于大数值的问题方面速度运算量是远低于暴力遍历的。
做题方面感觉二分的实现不难,但是切入点比较难找,例题:给出一条河,并给出n块石头距离河岸一头的距离,问搬去m块石头后,两块石头之间最短距离最大值为多少。
这道题一开始思考的时候想不到二分在哪使用,因为题目求解的问题只需按照距离由小到大删去就可以满足,所以打算用贪心,用差值数组把石头间的距离列出来,删去一个石头就把他的差值与上一个的差值合并,但是要先贪心排序,这样石头的顺序就打乱了,而且样例中还会出现连续多个石头被删去的情况,使用贪心实现比较困难,这时想到如果遍历答案的距离会不会简单一点,因为答案只有最短距离到河岸长度的范围大小,所以循环一次不会超时,这样就成了暴力遍历,所以就找到了了二分的使用之处,代码如下:

int find_(int be,int en)
{
    while(be<=en)
    {
        int mid=(be+en)/2;
        int c=0,st=0,la=1;
        while(la<n+1)
        {if(a[la]-a[st]<mid)
        {
            c++;
            la++;
        }
        else {st=la;
        la++;}}
        if(c>m)
            en=mid-1;
        else be=mid+1;
    }
    return en;
}

其中有两点需要注意,1:为了应对连续删去多块石头,石头距离变化的情况,将上一块石头设置为起始点,这块石头为结束点,如果当前石头被删去,则结束点移到下一块石头,判断这时候新构成的距离会不会满足当前答案,如果不满足即不删去,就让起始点和结束点同时改变,起始点继承结束点位置,而结束点位置变为下一个石块位置。2:这样二分可能会在一个区间里有多个解,所以要保证取到区间最大值,所以即使当前距离满足答案后也要再往后二分查找,找到最大值。
主函数代码:

int main()
{
    int w=0x3f3f3f3f;
    cin>>l>>n>>m;
    a[0]=0;
    a[n+1]=l;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n+1);
    for(int i=1;i<=n;i++)
    {
        w=min(w,a[i]-a[i-1]);
    }
    w=min(w,l-a[n]);
    cout<<find_(w,l)<<endl;
    return 0;
}

主函数中还有一步是去查找最小石块间距离,这一步要保证所有距离都被遍历,包括最后一块石头与河岸的距离。
例题:给出一个数,如果是素数就输出零,如果不是就输出离它最近的两个素数的差。
这道题类似于上一道搬石头的题,但是有素数处理这一步,之前做的有关判断素数的问题都是用一个循环来解决,从二除到他的算术平方根,若不能被整除,则说明是素数(2要单独判断),但是这个题需要找到素数,而不是判断素数,所以要打印素数表:

void build()
{
    memset(a,1,sizeof(a));
    for(long long i=2;i<=maxn/2;i++)
    {
        if(a[i]!=0)
        {for(long long j=2*i;j<=maxn;j+=i)
        {
            a[j]=0;
        }
        }
    }
    a[1]=1;
    a[2]=1;
}

这一步处理是第一次见到,本质上和判断素数是一样的,是对所有数的判断,这里用一个数的所有倍数来剔除所有非素数,值得注意的是倍数的循环是从二倍开始的,不然会误判除以自身也是非素数最后还要对2进行单独判断。(值得优化的地方还是挺多的,看了别人的代码发现对于这种大计算量的问题思维要更加缜密,不然很容易超时)
主函数代码:

int main()
{
    long long n;
    build();
    while(cin>>n)
    {
        if(n==0)
           break;
        if(a[n]==1)
            cout<<"0"<<endl;
        else
        {
            long long sum=2;
            long long be=n-1,en=n+1;
            while((be!=0)&&(a[be]==0))
            {
               be--;
               sum++;
            }
            while(a[en]==0)
            {
               en++;
               sum++;
            }
            cout<<sum<<endl;
        }
    }
    return 0;
}

主函数里值得时注意的点只有对距离的判断,首先是把两个最近的素数按照输入数分为两个区间,分别从两个区间判断距离,这是默认的距离应该为二因,然后左右遍历即可。这道题目虽然是二分的里的,但是想了很久没想到二分的具体应用方法,上网看了别人的二分方法,发现是二分查找距离给出数最近的素数,可以是比他大的那个素数,这时候只需用一个数组把所有素数存进去,然后让找到的最近的素数与比他小的那个素数相减就能得到答案,确实优化了代码,两次循环变为一次。
(附上别人二分的代码)

while (scanf("%d",&n)&&n)
      if (!have[n]) printf("0\n");
      else
      {
        l=1;
        r=tot;
        while (l<r)
        {
            mid=(l+r+1)/2;
            if (prm[mid]>=n) r=mid-1;
            else l=mid;
        }
        printf("%d\n",prm[l+1]-prm[l]);
      }

感觉二分的题目共同点比较多,实现的代码相似度也比较高,但是问题的形式比较绕,找出二分的点才是重点。

一周学习总结

这周主要是训练二分题目为主,同时也学习了搜索,包括DFS和BFS,一个深度优先搜索,一个广度优先搜索,还接触到了递归。递归类似于动态规划,都是将当前问题划分为多个小问题,然后对其求最优解,递归在实现深度搜索的时候很重要,深搜如图,即搜索每一条路线,从开始节点搜索到结束节点。在这里插入图片描述
回溯,当一个节点走完的时候,要返回到上一个分支点,选择另外一条线路再次开始走
深搜的代码模板:

int search(int k)
{
    int i;
    for (i=1;i<=n;i++)
     if  (!b[i])
      {
         a[k]=i;
         b[i]=1;
         if (k==r) print();
            else search(k+1);
         b[i]=0; 
      }
}

深搜的题目一般都是在求解可行方案数,广搜一般是最优解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值