您的位置:首页 > 文旅 > 美景 > 项目经理岗位职责_如何免费制作一个公司网站_百度大数据分析平台_口碑营销成功案例简短

项目经理岗位职责_如何免费制作一个公司网站_百度大数据分析平台_口碑营销成功案例简短

2025/4/6 5:04:26 来源:https://blog.csdn.net/weixin_60278491/article/details/146989478  浏览:    关键词:项目经理岗位职责_如何免费制作一个公司网站_百度大数据分析平台_口碑营销成功案例简短
项目经理岗位职责_如何免费制作一个公司网站_百度大数据分析平台_口碑营销成功案例简短

数据结构与算法学习笔记----贪心区间问题

@@ author: 明月清了个风
@@ first publish time: 2025.4.3

ps⭐️一个月没更了,三月初出去玩了,然后大论文写到现在哈哈哈

贪心问题其实不像其他问题能够有非常具体的解决思路,往往是通过尝试找到一种方法后再证明其正确性,贪心的关键在于关注最优子结构向最优全局解的证明。

Acwing 905. 区间选点

[原题链接](905. 区间选点 - AcWing题库)

给定 N N N个闭区间 [ a i , b i ] [a_i, b_i] [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。

位于区间端点上的点也算作区间内。

输入格式

第一行包含整数 N N N,表示区间数。

接下来 N N N行,每行包含两个整数 a i , b i a_i, b_i ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示所需的点的最小数量。

数据范围

1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105

− 1 0 9 ≤ a i ≤ b i ≤ 1 0 9 -10^9 \le a_i \le b_i \le 10^9 109aibi109

思路

这里先直接给出结论:

  1. 将所有区间按右端点从小到大排序。
  2. 从前往后依次枚举每个区间,如果当前区间中已经包含点,则不选择;若不包含点,则选择当前区间的右端点。

接下来证明此策略的正确性:

因为我们要选择尽可能少的点,因此目标也就是使选择的每个点覆盖尽可能多的区间

  • 首先进行贪心选择的正确性证明,也就是为什么选择右端点?

    对于第一个区间 [ a 1 , b 1 ] [a_1,b_1] [a1,b1]来说,其右端点 b 1 b_1 b1经过排序后为所有区间的右端点中的最小值,那么剩余区间就只有两种情况,一是与 [ a 1 , b 1 ] [a_1,b_1] [a1,b1]有交集,也就是 a i < b 1 a_i < b_1 ai<b1;二是与 [ a 1 , b 1 ] [a_1,b_1] [a1,b1]无交集,也就是 a i > b 1 a_i > b_1 ai>b1。那么对于第一种情况,当我们选择了右端点 b 1 b_1 b1后,该区间一定包含 b 1 b_1 b1,所以不用选择点;对于第二种情况,该区间不包含 b 1 b_1 b1,一定会另选点。也就是说,当我们选择了第一个区间的右端点 b 1 b_1 b1后,我们就覆盖了所有左端点 a i < b 1 a_i < b_1 ai<b1的区间,并且我们选择了是在排序后最靠前的 b 1 b_1 b1,也意味着为后续区间留下了更大的右端点空间。

    由第一个区间进行推广,假设前 k k k个点的选择都是像上面的局部最优解,那么对于第 k + 1 k + 1 k+1个点,我们选择的一定是未被覆盖的区间中的右端点最小的点,与上面的选择一致,因此正确。

  • 然后证明全局的最优性:

    也就是假设存在一个最优解,其选择的点数 m ′ m' m小于根据上面的选法选出的点数 m m m,证明这是不可能的。

    根据我们的选择方案,我们选择的每个点所在的区间一定是没有交集的,因为当我们选择一个点后,下一个区间的左端点如果小于该点,则一定不会被选择,若被选择,一定是因为该区间的左端点大于了上一个选择的点,也就意味着覆盖所有的区间至少需要 m m m个点,因此不存在更优解。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 100010;int n;
struct Range
{int l, r;bool operator < (const Range &W)const{return r < W.r;}
}range[N];int main()
{cin >> n;for(int i = 0; i < n; i ++){int l, r;cin >> l >> r;range[i] = {l, r};}sort(range, range + n);int res = 0, ed = -2e9;for(int i = 0; i < n; i++){if(range[i].l > ed){res ++;ed = range[i].r;}}cout << res << endl;return 0;
}

Acwing 908. 最大不相交区间数量

[原题链接](908. 最大不相交区间数量 - AcWing题库)

给定 N N N个闭区间 [ a i , b i ] [a_i, b_i] [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

输出可选取区间的最大数量。

输入格式

第一行包含整数 N N N,表示区间数。

接下来 N N N行,每行包含两个整数 a i , b i a_i, b_i ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示可选取区间的最大数量。

数据范围

1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105

− 1 0 9 ≤ a i ≤ b i ≤ 1 0 9 -10^9 \le a_i \le b_i \le 10^9 109aibi109

思路

直接说结论吧这一题,做法和上一题完全一致,回顾一下上一题的思路其实也能看出来。

同样可以从两个方向来证明,

根据上一题选法,选择的相邻的两个点一定是没有交集的两个区间的点,因此就可以认为我们的答案 a n s ans ans一定是一种可行方案,且 a n s ≥ c n t ans \ge cnt anscnt

下一步证明 a n s ≤ c n t ans \le cnt anscnt,这里可以用反证法,假设我们的答案 a n s ans ans是大于 c n t cnt cnt的,也就意味着可以选择更多的存在于没有交集的区间的点,但是根据我们的选法来看,该选法覆盖了所有的区间,不存在额外的区间中的点,因此得证。

代码和上一题一样。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 100010;int n;
struct Range
{int l, r;bool operator < (const Range &W)const{return r < W.r;}
}range[N];int main()
{cin >> n;for(int i = 0; i < n; i ++){int l, r;cin >> l >> r;range[i] = {l, r};}sort(range, range + n);int res = 0, ed = -2e9;for(int i = 0; i < n; i++){if(range[i].l > ed){res ++;ed = range[i].r;}}cout << res << endl;return 0;
}

Acwing 906. 区间分组

[原题链接](906. 区间分组 - AcWing题库)

给定 N N N个闭区间 [ a i , b i ] [a_i, b_i] [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数。

输入格式

第一行包含整数 N N N,表示区间数。

接下来 N N N行,每行包含两个整数 a i , b i a_i, b_i ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示最小组数。

数据范围

1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105

− 1 0 9 ≤ a i ≤ b i ≤ 1 0 9 -10^9 \le a_i \le b_i \le 10^9 109aibi109

思路

同样直接给出思路,因为是经典的模型。

  1. 将所有区间按左端点从小到大排序
  2. 从前往后处理每个区间,判断能否放在某个组里,也就是判断当前区间的左端点是否大于该组中的最大的右端点
  3. 如果上一步中未能找到一个组,那么就开一个新的组。

下一步就是证明该做法的正确性,首先证明我们的算法得到的 c n t cnt cnt是否大于等于正确答案 a n s ans ans,因为选取的规则保证了能够按照题意得到结果,因此肯定是一种合法方案,因此 c n t ≥ a n s cnt \ge ans cntans肯定成立

而对于 c n t ≤ a n s cnt \le ans cntans可以从特殊时间节点来看,也就是创建第 c n t cnt cnt个组的时刻,当我们新开第 c n t cnt cnt个组时,根据选法来看,保证了前 c n t − 1 cnt - 1 cnt1个组的最大右端点值一定大于当前这个区间的右端点,因此无法放入任何一个组,进而新开了第 c n t cnt cnt个组,这也意味着这 c n t cnt cnt个组的第一个区间一定是相互之间有交集的,也就是新开到第 c n t cnt cnt个组的这个区间的左端点一定被包含在了前 c n t cnt cnt个组里,因此得证。

还有一个问题就是,如果存在多个能够放入的组应该放在哪一个,会不会影响最终的结果,答案是不会,任取一个就行,体现在代码中就是选最小的组。这个问题的核心就是存在多个可用组的情况下,为什么选择任意一个得到的答案都是最小的,这是因为答案其实就是所有区间的最大重叠数,也就是最多的存在同一点的区间数。假设这个数是 k k k,那么说明有 k k k个区间存在相交点,那么就至少需要 k k k组才能容纳这 k k k个组,而剩余区间的重叠数一定小于等于 k k k,因此可以分别分到这 k k k个组中。

代码

#include <iostream>
#include <algorithm>
#include <queue>using namespace std;const int N = 100010;int n;
struct Range
{int l, r;bool operator< (const Range &W)const{return l < W.l;}
}range[N];int main()
{scanf("%d", &n);for(int i = 0; i < n; i ++){int l, r;cin >> l >> r;range[i] = {l, r};}sort(range, range + n);priority_queue<int, vector<int>, greater<int>> heap;for(int i = 0; i < n; i ++){auto r = range[i];if(heap.empty() || heap.top() >= r.l) heap.push(r.r);else{int t = heap.top();heap.pop();heap.push(r.r);}}printf("%d\n", heap.size());return 0;
}

Acwing 907. 区间覆盖

[原题链接](907. 区间覆盖 - AcWing题库)

给定 N N N个闭区间 [ a i , b i ] [a_i, b_i] [ai,bi]以及一个区间 [ s , t ] [s,t] [s,t],请你选择尽量少的区间,将指定区间完全覆盖。

输出最少区间数,如果无法完全覆盖则输出 − 1 -1 1

输入格式

第一行包含两个整数 s s s t t t,表示给定区间的两个端点。

第二行包含整数 N N N,表示给定区间数。

接下来 N N N行,每行包含两个整数 a i , b i a_i, b_i ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示所需最少区间数。

如果无解,则输出 − 1 -1 1

数据范围

1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105

− 1 0 9 ≤ a i ≤ b i ≤ 1 0 9 -10^9 \le a_i \le b_i \le 10^9 109aibi109

− 1 0 9 ≤ s ≤ t ≤ 1 0 9 -10^9 \le s \le t \le 10^9 109st109

思路

同样地给出思路:

  1. 将所有区间按左端点从小到大排序
  2. 从前往后枚举每个区间,在所有能覆盖 s t st st的区间中选择右端点最大的区间,然后将 s t st st更新为这个值

这个思路很容易证明是正确的,首先选法一定是合法的,并且保证了每一步的覆盖范围最大,且当前步骤的选取不会影响后续的选择,因为已经排过序了。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 100010;int n;
struct Range
{int l, r;bool operator< (const Range &W)const {return l < W.l;}
}range[N];int main()
{int st, ed;scanf("%d%d", &st, &ed);scanf("%d", &n);for(int i = 0; i < n; i ++){int l,r ;cin >> l >> r;range[i] = {l, r};}sort(range, range + n);int res = 0;bool success = false;for(int i = 0; i < n; i ++){int j = i, r = -2e9;while(j < n && range[j].l <= st){r = max(r, range[j].r);j ++;}if(r < st){res = -1;break;}res ++;if(r >= ed){success = true;break;}st = r;i = j - 1;}if(!success) res = -1;cout << res << endl;return 0;}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com