P1824洛谷题解

主要想借这道题对二分答案的区间问题进行一个反思和总结

代码
#include<bits/stdc++.h>
using namespace std;
int N, C;
int point[100008];
bool check(int y)
{
    int cnt=0;
    int i;
    int last=point[1];
    for (i = 2; i <= N; i++)
    {
        if (point[i] - last < y)
        {
            cnt++;
        }
        else
            last = point[i];
    }
    if (cnt <= N - C)return 1;
    else return 0;
}
int main()
{
    cin >> N >> C;
    int i;
    for (i = 1; i <= N; i++)
    {
        cin >> point[i];
    }
    sort(point+1, point+1 + N);
    int l = 1, r = point[N]-point[1];
    int cc = -1000;
    while (l < r)
    {
        
        int mid = (l+r)/2;
        if (check(mid))
        {
            
            l = mid + 1;
        }
        else
            r = mid -1;
    }
    cout << l;
}

情况一:

看这块核心代码:

 while (l < r)
    {
        
        int mid = (l+r)/2;
        if (check(mid))
        {
            
            l = mid + 1;
        }
        else
            r = mid -1;
    }

分析一下这种情况:当check(mid)函数返回1的时候说明mid是一个可行解,但不知道是不是一个最优解。本题中要求的是最短距离的最大值。格外留意这个最大值。此时如果mid是一个符合题目调节的最短距离的值但是我们不知道在[mid,right]中还有没有比mid更大的可行解就是我们所说的最优解问题。所以说l=mid+1这种写法是错误的。mid也有可能是最优解所以我们寻找最优解的区间应该是[mid,right]而不是[mid+1,right].当check(mid)函数返回0时说明mid这个解不符合题意所以说我们寻找的区间就没有必要包括mid了而是[left,mid-1].

情况二:

作如下改正:

while (l < r)
    {
        
        int mid = (l+r)/2;
        if (check(mid))
        {
            
            l = mid ;
        }
        else
            r = mid -1;
    }

按照上面所说这就是对的吗?其实还是ac不了。分析一下当left=right-1时mid=(left+right)/2=left;因为(left+right)/2就是一个下取整函数。所以说l=mid而我们结束循环的条件是left<right此时就会产生死循环,从而造成了测评结果是TLE.所以我们还要继续改正那原因出在哪呢?[left,right]我们采用的是闭区间的二分当区间二分到足够小时有可能会发生死循环。那么如果我们将闭区间换成开区间呢。[left,right),mid=(left+right)/2,换成

若mid为解left=mid+1,

mid不为解则right=mid.

结束循环的条件为right=left+1;此时left就是最优解。

开区间是要比闭区间“宽松”可以避免死循环。

while (l+1 < r)
    {
        
        int mid = (l+r)/2;
        if (check(mid))
        {
            
            l = mid+1 ;
        }
        else
            r = mid ;
    }

这种写法也是正确的,但是也没那么好区分下面介绍一种方便记忆的写法。

情况三:

我们定义一个ans来储存我们的最优解就是最短距离的最大值。

若mid为可行解

则更新ans ,ans=max(ans,mid)

我们就将mid存储在了ans中下一步我们该搜索的区间就不用包含mid了应为[mid+1,right]

若mid为无效解

right=mid-1我们的搜索区间就变为了[left,mid-1]

我们将这种写法和第一种写法进行对比一下看看还会不会发生死循环的情况。当left=right-1时mid=(left+right)/2=left,赋值left=mid+1=right此时left=right.但是别玩了修改最原始循环的条件left<right为left<=right此时这个值也是要去检验的所以变成了while(left<=right)

附上代码:

int ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)
{
ans=max(ans,mid);
l=mid+1;
}
else
r=mid-1;
}
cout<<ans

总结:

整数二分这一块怎么取区间还是比较麻烦的,第三种方法还是比较好的。看大家能理解哪种,适合自己的就好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值