贪心算法2 ta来啦!!!
对贪心基础思想和基础问题还不理解的请回看上一次的
OK,难度升级
我们先来看线段覆盖类问题
5220 摄像头1
在一条长度为𝐿的笔直的公路上安装若干个摄像头,用于监控交通状况。我们可以把这条公路看作数轴[0,𝐿]。何老板承包了这项工程,但交管部门对摄像头的安置提出了𝑛个要求,每个要求形如[𝑥,𝑦],表示在[𝑥,𝑦]这段区间至少要安置一个摄像头。 何老板想要用尽可能少的成本完成这项工程,因此,他想知道,最少需要安装多少个摄像头? 数据范围:𝑛≤10^5,0≤𝑥≤𝑦≤10^9
数据范围:𝑛≤10^5,0≤𝑥≤𝑦≤10^9
输入格式: 第一行,一个整数𝑛,表示有𝑛个要求需要满足。 接下来𝑛行,每行两个整数𝑥和𝑦,表示一个要求,即𝑥,𝑦这段区间至少要求一个摄像头。
输入格式: 一行,一个整数,表示最少所需摄像头的个数。
样例输入:
4
3 6
2 4
0 2
4 7
样例输出
2
解析:
从左到右安装摄像头,所以将𝑛个要求从左到右排序?
贪心策略:?
1. 将线段按从左到右排序 按左端点排序?按右端点排序? 待定
2. 枚举线段,如果需要新摄像头,尽量靠右 新摄像头放在这条线段的右端点。
3. 推断排序规则:
解析: 从左到右安装摄像头,所以将𝑛个要求从左到右排序?
贪心策略:
将𝑛个需求按右端点从小到大排序 依次枚举需求,如果没有被满足,就在它的右端点装一个摄像头 判断有没有被满足,只需要检查已安装的最右边一个摄像头是否在范围内
间复杂度𝑂𝑛log𝑛。
重点代码
sort(D+1,D + n + 1, cmp); //按区间右端点由小到大排序,区间i的左右界分别是D[i].Left, D[i].Right
int cnt = 1;
int Now = D[1].Right; //Now表示当前安装摄像头的位置
for(i = 2; i <= n; i++) {
if( Now < D[i].Left ) { //i号区间没有被摄像头覆盖
cnt++;
Now = D[i].Right; //新安装摄像头的位置
}
}
5221 摄像头2
NK中学里有一条长度为L的笔直道路,同学们可以把该路看作数轴,路的一段坐标为0,一段坐标为L,表示区间[0,L], 在这条路上安装有n个摄像头,每个摄像头都有一定的拍摄区间,第i个摄像头覆盖的区间为[Xi,Yi]。本着节约用电的态度,何老板想知道,最少开启几个摄像头就可以将整个这条路都置于视频监控中?请你帮他回答。 数据范围:1<=n<=100000 ; 1<=L<=1000000000; 0<=x<=y<=1000000000
解析: 最小线段覆盖问题:最少几条线段就能覆盖整个指定区间
已知:x1 < x2 < x3 < x4 < x5 < x6
考虑1:有如下四条线段,优先选哪一条?
[x1,x2] [x1,x4] [x2,x3] [x2,x4]
显然,选[x1,x4],即左端点越小越好,若相同,右端点越大越好。
考虑2:若已选[x1,x4],如下三条线段,优先选哪一条?
[x2,x5] [x3,x5] [x3,x6]显然,选[x3,x6]
即选左端点在区间[x1,x4]中且右端点尽可能大的线段
贪心算法:
1.将线段按左端点由小到大排序,若左端点相同,按右端点由大到小排序(这是根据考虑1的结论); 2.从左往右讨论每条线段,优先选择右端点大的线段:
记录目前已选线段中,往右最远能覆盖到的位置NowFar
讨论所有左端点<=NowFar且未被讨论过的线段,记录其中右端点的最大值MaxRight(这是根据考虑2的结论)
若MaxRight>NowFar,则将NowFar=MaxRight; 否则无解。
重点代码
int solve() {// 数 组 D[] 记 录 每 条 线 段 , 已 排 序 ,D[i].L 和 D[i].R 记 录 i 号 线 段 的 左 右 端 点 。
if(D[1].L>0) return -1; //本题覆盖的起点从0开始
int cnt=1;
int NowFar = D[1].R; //NowFar记录当前覆盖到的位置,即[0,NowFar]都被覆盖了。
int MaxRight = D[1].R;
if ( NowFar>=Len ) return cnt;
int i=2;
while(true){ //下面for循环找出所有左端点<=Now且未被讨论过的线段中,右端点的最大值
for(;i<=n&&D[i].L<=NowFar; i++)
MaxRight=max(MaxRight,D[i].R);
if(MaxRight<=NowFar) return -1; //说明区间断开了,无法连续覆盖
cnt++;
NowFar=MaxRight;
if(NowFar>=Len) return cnt;
}
return -1;
}//每条线段只有一次被讨论的机会,时间复杂度O(n)
再看一个调度问题
样例输入
3 2
1 2 3
样例输出
1
大概能看出策略:
当有空闲的机器的时候,优先加工所需时间长的工件; 当没有空闲机机器的时候,等待加工完毕就有空闲机器了
贪心策略:
当有空闲的机器的时候,优先加工所需时间长的工件; 每次的工件都选择当前最快的生产线(尽快结束前面加工任务的生产线)
重点代码
cin>>n>>r;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=n;i>=1;--i){
a1[1]+=a[i];
sort(a1+1,a1+1+r);
}
cout<<a1[r];
贪心就讲到这里
(记得多做题)