贪心+二分

疯牛

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入
有多组测试数据,以EOF结束。
第一行:空格分隔的两个整数N和C
第二行——第N+1行:分别指出了xi的位置
输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1
2
8
4
9
样例输出
3
来源

POJ 翻译


这道题拿到手之后,那叫一个愣。什么鬼?什么鬼?完全不知道什么意思。后来就完全懂了

题意:

把C头牛放到N个带有编号的隔间里,使得任意两头牛所在的隔间编号的最小差值最大。例如样例排完序后变成1 2 4 8 9,那么1位置放一头牛,4位置放一头牛,它们的差值为3;最后一头牛放在8或9位置都可以,和4位置的差值分别为4、5,和1位置的差值分别为7和8,不比3小,所以最大的最小值为3。



思想:

二分+贪心


考察:
最大化的最小值。


二分思想:
就是找一个左端点和一个右端点然后不不断地进行分下去。


代码::

int n,c;
int a[1000005];
bool get(int x)
{
    int cun=1,t=a[0];
    for(int i=1;i<n;i++)
    {
        if(a[i]-t>=x)
        {
            cun++;
            t=a[i];
        }
            if(cun>=c)
            return true;
    }
    return false;
}
int erfen()
{
    int l=0,r=a[n-1]-a[0];
    int mid;
    while(l<=r)
    {
       mid=(l+r)/2;
        if(get(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    return l-1;
}
int main()
{
    while(~scanf("%d%d",&n,&c))
    {


        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        sort(a,a+n);
        printf("%d\n",erfen());
    }
}

<think>好的,用户现在想了解如何用Python实现“解法二:贪心+二分优化(O(n log n))”来解决最长递增子序列问题,特别是在处理二维数据(如长方形的长宽)时的应用。用户之前已经询问过为什么在高度相等的情况下要按宽度降序排列,现在他们可能是在处理类似俄罗斯套娃信封或者需要二维排序的问题。 首先,我需要确定用户的需求。他们可能是在解决LeetCode上的某个问题,比如第354题“俄罗斯套娃信封问题”,这题需要将信封按长宽排序后,转化为最长递增子序列问题。解法二中的排序预处理,按长升序、宽降序,这样可以保证在长相等时,宽降序,这样后续处理时不会出现长相同的情况,从而正确应用贪心+二分的优化方法。 接下来,用户希望具体的Python实现步骤。我需要先解释排序的逻辑,然后是如何维护tail数组,以及二分查找的过程。需要注意的是,在Python中可以使用bisect模块来简化二分查找的实现。 然后,我需要考虑用户可能的疑问点。例如,为什么排序时要按长升序、宽降序?这是因为当长度相同时,宽度较大的排在前面,这样在后续处理宽度时,不会出现相同长度的信封被错误地计算为递增的情况,从而保证每个长度只保留一个最小的宽度,避免重复。 另外,用户可能对tail数组的作用不太清楚,需要解释tail数组保存的是长度为i+1的子序列的最小末尾值,这样可以在后续的元素中更容易地形成更长的子序列。 在代码实现部分,需要展示如何导入bisect模块,如何排序,遍历宽度数组,以及如何处理每个元素。同时,要提到bisect.bisect_left的使用,以及替换或追加元素到tail数组的逻辑。 最后,可能需要给出一个具体的例子,比如用户提到的排序后的宽度数组,然后逐步演示tail数组的变化过程,这样用户能更直观地理解算法是如何工作的。 此外,还要考虑到用户可能对时间复杂度的疑问,解释为什么这种方法的时间复杂度是O(n log n),主要是因为排序的时间是O(n log n),而遍历二分查找的时间也是O(n log n)。 总结来说,用户需要一个清晰的步骤解释可运行的Python代码示例,同时希望理解背后的原理每一步的作用。需要确保代码正确,并且解释到位,避免他们在实际应用中出现错误。</think>以下是针对**最长递增子序列(LIS)**问题的贪心+二分优化解法(时间复杂度 $O(n \log n)$)的逐步解释及Python实现,适用于二维排序场景(如俄罗斯套娃信封问题): --- ### **1. 问题背景** 在二维排序问题中(例如信封的长宽),若需找到**严格递增的子序列**,需先将元素按特定规则排序,再转化为一维的LIS问题。例如: - **排序规则**:按长升序排列,若长相等则按宽降序排列(避免相同长的元素干扰后续计算)。 --- ### **2. 算法核心思想** 1. **排序预处理**:将元素按长升序、宽降序排列。 2. **贪心+二分维护递增序列**:维护一个数组 `tail`,`tail[i]` 表示长度为 `i+1` 的递增子序列的最小末尾值。 - 若当前元素大于 `tail` 末尾,直接追加。 - 否则,用二分查找替换 `tail` 中第一个不小于当前元素的值。 3. **结果**:`tail` 的长度即为最长递增子序列的长度。 --- ### **3. Python实现代码** ```python import bisect def max_envelopes(envelopes): # 1. 排序预处理:长升序,长相同则宽降序 envelopes.sort(key=lambda x: (x[0], -x[1])) # 2. 提取宽度序列,转化为LIS问题 widths = [env[1] for env in envelopes] # 3. 贪心+二分维护递增序列 tail = [] for w in widths: idx = bisect.bisect_left(tail, w) # 找到第一个 >=w 的位置 if idx == len(tail): tail.append(w) else: tail[idx] = w # 替换为更小的w,优化后续可能性 return len(tail) ``` --- ### **4. 逐步解析** #### **步骤1:排序预处理** ```python envelopes.sort(key=lambda x: (x[0], -x[1])) ``` - **长升序**:保证后续只需关注宽度的递增。 - **长相同则宽降序**:避免相同长的元素形成无效递增(例如 `(5,4)` `(5,6)`,若宽升序排列会导致误判)。 #### **步骤2:提取宽度序列** ```python widths = [env[1] for env in envelopes] ``` - 排序后,问题转化为在宽度序列 `widths` 中找**最长递增子序列**。 #### **步骤3:贪心维护递增序列** ```python tail = [] for w in widths: idx = bisect.bisect_left(tail, w) if idx == len(tail): tail.append(w) else: tail[idx] = w ``` - **bisect_left**:找到 `tail` 中第一个 `>=w` 的位置。 - **追加或替换**: - 若 `w` 比 `tail` 末尾大,直接追加(形成更长的子序列)。 - 否则,替换 `tail` 中第一个 `>=w` 的值,使 `tail` 的末尾尽可能小,为后续留出空间。 --- ### **5. 示例演示** 假设输入 `envelopes = [[5,4],[6,4],[6,7],[2,3]]`: 1. **排序后**:`[[2,3], [5,4], [6,7], [6,4]]`(长升序,长相同则宽降序)。 2. **提取宽度**:`widths = [3, 4, 7, 4]`。 3. **维护 `tail`**: - `w=3` → `tail = [3]` - `w=4` → 追加 → `tail = [3,4]` - `w=7` → 追加 → `tail = [3,4,7]` - `w=4` → 替换 `tail[1]` → `tail = [3,4,7] → [3,4,7]` 4. **结果**:`tail` 长度为3,即最长递增子序列为 `[3,4,7]`。 --- ### **6. 时间复杂度** - **排序**:$O(n \log n)$ - **遍历+二分查找**:$O(n \log n)$ - **总复杂度**:$O(n \log n)$ --- ### **7. 应用场景** - **俄罗斯套娃信封问题**:需同时满足长宽递增。 - **任务调度**:按特定维度排序后找最长可行序列。 - **动态规划优化**:将 $O(n^2)$ 的DP解法优化为 $O(n \log n)$。 --- ### **总结** 通过排序预处理将二维问题转化为一维LIS,再利用贪心+二分的策略维护递增序列,既保证了时间复杂度最优,又简化了问题逻辑。此方法在LeetCode等算法题中广泛适用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值