贪心是指每次找局部最优解,就可以找到全局最优解,所以贪心一般要求函数是单峰的。
动态规划的限制会少一些,一般是枚举了空间中的所有值,找出了最优解。
一、区间选点
给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需的点的最小数量。
数据范围
1≤N≤10^5,
−10^9≤ai≤bi≤10^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
分析:1.将每个区间按右端点从小到大排序
2.从前往后枚举每个区间、
(1)如果当前区间已经包含点,则直接pass
(2)否则,选择当前区间的右端点
ans表示我们想要的最优解
cnt表示选择的区间个数
证明 : ans<=cnt
这个证明用的是2.(1)条件:
cnt表示当前选择一种可行性方案解,所选的区间的个数
因为cnt是所有可行性方案,所以ans<=cnt
证明 : ans>=cnt
这个证明用的是2.(2)条件:
因为我们所有区间要满足2.(2)的条件,那么,所有区间是没有重合的部分的,我们要想包含所有区间,就必须选择所有区间,区间个数是cnt
那么我们任何一个方案,点的个数必须大于等于cnt,点的个数可以超过cnt,因为这时,满足题意每个区间至少有一个点。所以ans>=cnt
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
struct Range{
int l,r;
bool operator < (const Range &W) const
{
if(r==W.r) return l<W.l;
return r<W.r;
}
}range[N];
int n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d",&range[i].l,&range[i].r);
sort(range,range+n);
int ed=-2e9;
int res=0;
for(int i=0;i<n;i++)
{
if(ed<range[i].l)
{
res++;
ed=range[i].r;
}
}
cout<<res<<endl;
return 0;
}
二、最大不相交区间数量
给定 N 个闭区间 [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示可选取区间的最大数量。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
此题可以对应的实际问题:同时有很多课,我们想要选尽可能多的课。
分析:和第一题一样,第一题是按右端点排序,如果出现一个区间的左端点大于此时区间的右端点,那么res++;
而这一题:选尽可能多的区间,那么如果一个区间的左端点比当前枚举区间的右端点小,那么这两个区间一定不能同时选,所以和第一题代码一样。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
struct Range{
int l,r;
bool operator < (const Range &W) const{
return r<W.r;
}
}range[N];
int n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d%d",&range[i].l,&range[i].r);
sort(range,range+n);
int res=0,ed=-2e9;
for(int i=0;i<n;i++)
{
if(ed<range[i].l)
{
res++;
ed=range[i].r;
}
}
cout<<res<<endl;
return 0;
}
三、区间分组
(1)
给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示最小组数。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
有若干个活动,第i个活动开始时间和结束时间是[Si,fi],同一个教室安排的活动之间不能交叠,求要安排所有活动,少需要几个教室?
有时间冲突的活动不能安排在同一间教室,与该问题的限制条件相同,即最小需要的教室个数即为该题答案。
注意题目:不能有交集
也就是说[1,4],[4,5],答案为2;
变为{2,8,9,11};
本题可以巧妙的处理起点和终点相同的问题
巧妙做法代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N*2],idx;
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[idx++]=x*2;//左端点全部设为偶数
a[idx++]=y*2+1;//右端点全部设为奇数
}
sort(a,a+idx);
int res=0,t=0;
for(int i=0;i<idx;i++)
{
if(a[i]%2==0) t++;
else t--;
res=max(res,t);
}
cout<<res<<endl;
return 0;
}
朴素做法
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e5 + 10;
struct Range{
int l,r;
bool operator < (const Range &W) const{
return l<W.l;
}
}range[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d",&range[i].l,&range[i].r);
sort(range,range+n);
priority_queue<int,vector<int>,greater<int>> heap;//小根堆的作用是每次比较时,用的都是所有组中最小的右端点
for(int i=0;i<n;i++)
{
if(heap.empty()||heap.top()>=range[i].l) heap.push(range[i].r);
else
{
heap.pop();
heap.push(range[i].r);
}
}
cout<<heap.size()<<endl;
return 0;
}
Schedules (nowcoder.com)https://ac.nowcoder.com/acm/problem/25346
本题是 可以有交集的,即时间为[1,4],[4,5],答案为1;
代码
#include <bits/stdc++.h>
using namespace std;
int vis[100005];
int main() {
int n, l, r, maxx = 0, max_ = 0;
scanf("%d",&n);
for (int i = 0; i < n; i++) {
scanf("%d%d", &l, &r);
max_ = max(max_, r);
vis[l]++;
vis[r]--;
}
for (int i = 1; i <= max_; i++) {
vis[i] += vis[i - 1];
maxx = max(maxx, vis[i]);
}
printf("%d\n", maxx);
}
四、区间覆盖
给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出 −1。
输入格式
第一行包含两个整数 s 和 t,表示给定线段区间的两个端点。
第二行包含整数 N,表示给定区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需最少区间数。
如果无解,则输出 −−1。
数据范围
1≤N≤105,
−109≤ai≤bi≤109,
−109≤s≤t≤109
输入样例:
1 5
3
-1 3
2 4
3 5
输出样例:
2
算法思路:
1.先把所有区间按照左端点从小到大排序。
2.找到满足条件的区间(区间的左端点小于等于线段的左端点),从所有满足条件的区间中找到右端点最大的区间。(这一步用到了贪心的思想)
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
struct Range {
int l,r;
bool operator < (const Range &W) const
{
return l<W.l;
}
}range[N];
int main()
{
int n;
int st,ed;
scanf("%d%d",&st,&ed);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);
int res=0,flag=0;
for(int i=0;i<n;i++)
{
int j = i , r=-2e9;//每次初始r的值,是因为当r=st,且下边已经没有区间时,不初始就不会break
while(j < n && range[j].l <= st)
{
r=max(r,range[j].r);
j++;
}
if(r < st)
{
res=-1;
break;
}
res++;
if(r>=ed)
{
flag=1;
break;
}
st=r;
i=j-1;
}
if(flag==0) res=-1;
printf("%d\n",res);
return 0;
}