——以“今年暑假不AC 和 打扫牛棚”为例
这类线段覆盖问题求最大最小值大同小异
今年暑假不AC
题目描述
“今年暑假不AC?”
“是的。”
“那你干什么呢?”
“看世界杯呀,笨蛋!”
“@#$%^&*%...”
确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。 作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)
输入格式
输入数据包含多个测试实例,每个测试实例的第一行只有一个整数\(n(n<=100)\),表示你喜欢看的节目的总数,然后是\(n\)行数据,每行包括两个数据\(Ti_s,Ti_e\) \((1<=i<=n)\),分别表示第\(i\)个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。\(n=0\)表示输入结束,不做处理。
输出格式
对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行
题目分析
先化解题意(这题B话属实多)
有\(n\)个线段,分别给定长度。要求最多能在其中选取多少条使得他们不首尾覆盖地排列
注意:\([1,i]与[i,j]\)相互之间不覆盖
那么这就是到贪心算法的用武之地了,贪心算法的核心是要去作出当前情况下所能作出的最“贪”的选择,而不用考虑之后的影响。说白了,就是你面前有一张10块和一张100。那肯定是拿那张100的,至于那张10块的,谁去拿,他够不够都不在我们的考虑范围之内。
就这一题而言,我现在肯定优先选择我当前情况下,所能选取的(开始时间肯定要大于等于我上个节目的结束时间),结束时间最早的节目去看。
那么,代码就顺理成章了
Accepted Code
/* * @Author: Gehrychiang * @Date: 2020-01-21 13:17:11 * @Website: www.yilantingfeng.site * @E-mail: gehrychiang@aliyun.com */ #include <bits/stdc++.h> using namespace std; struct $ { int st; int en; }; $ cls[1000005]; bool comp($ a, $ b) { if (a.en != b.en) { return a.en < b.en; } else { return a.st < b.st; } } int main() { int n; while (cin >> n, n != 0) { memset(cls, 0, sizeof(cls)); for (int i = 0; i < n; i++) { cin >> cls[i].st >> cls[i].en;//读取 } sort(cls, cls + n, comp);//按照结束时间优先排序,将结束的早的放在前面 int cur_end = 0;//记录我当前的结束时间 int cnt = 0; for (int i = 0; i < n; i++) { if (cls[i].st >= cur_end)//这个可以选取 { cur_end = cls[i].en;//更新结束时间 ++cnt; } } cout << cnt << endl; } return 0; }
打扫牛棚
题目描述
大表哥分配 N \((1 <= N <= 25000)\) 只中的一些奶牛在牛棚附近做些清洁。 他总是要让至少一只牛做清洁。他把一天分成T段\((1 <= T <= 1000000)\), 第一段是\(1\),最后一段是\(T\)
每只奶牛只在一些时间段有空。奶牛如果选择某一段时间,则必须完成整段时间的工作
你的任务是帮助他安排一些奶牛,使每段时间至少有一只奶牛被安排来做这件事。并且奶牛数应尽可能小。如果不可能办到,输出\(-1\)
输入格式
* 第一行:\(N\)和\(T\)
* 第二行至\(N+1\)行: 每一行包括奶牛能工作的开始和结束时间。闭区间。
输出格式
每组数据一行,输出完成清洁所需最少的奶牛数,如果不可能办到,输出\(-1\)
题目分析
首先把题目翻译成人话
有一段从\(1\)到\(n\)的区间,现有多条线段(左右界不同,有重合区域),求最少需几条线段才能覆盖整个大区间?
并且根据这个题目的描述。这样的两个区间如\([1, 3]\) , \([4, 7]\)也算覆盖了\([1,7]\)
既然要达到一个最短覆盖数的效果,那么贪心策略也是比较显然的,我们肯定是要优先去选择更长的线段(直观来看)。说成比较严谨的语言,就是我要选取当前能够选取的所有情况中,结束点最大的那一条线段。
这样的实现也并不复杂,我们还是和上一条一样,利用一个cur_end变量去记录当前的结束位置即可。
需要注意的几个情况主要有出现间断点的情况,这个及时break
Accepted Code
/* * @Author: Gehrychiang * @Date: 2020-02-07 13:17:49 * @Website: www.yilantingfeng.site * @E-mail: gehrychiang@aliyun.com */ #include <iostream> #include <algorithm> #include <cstdio> using namespace std; struct $ //结构体存储每一个奶牛的开始时间和结束时间 { int s; int e; }; $ cow[25005]; bool cmp($ a, $ b) { if (a.s != b.s) { return a.s < b.s; } else { return a.e > b.e; } } int main() { //freopen("","r",stdin); //freopen("","w",stdout); int n, t; scanf("%d %d", &n, &t); for (int i = 0; i < n; i++) { scanf("%d %d", &cow[i].s, &cow[i].e); } //每一次都优先选择起始点比最大,结束时间最大 sort(cow, cow + n, cmp); //优先按照开始时间排序,当开始时间相同时优先排结束时间最晚的 int cur_end = 0; int cur_end_max = 0; int cnt = 0; for (int i = 0; i < n; i++) { if (cow[i].s <= cur_end + 1) //遍历所有的能够选取的情况 { cur_end_max = max(cow[i].e, cur_end_max); //选取最大值 if (cur_end_max >= t) { ++cnt; break; } } else //已经把所有当前能够选取的情况遍历过了,需要更新情况 { if (cur_end == cur_end_max) //没有得到任何变化直接break也就是说我到这个位置就已经结束位置就增加不上去了(或者说是存在了间断) { break; } cur_end = cur_end_max; //更新当前的结束位置 cur_end_max = 0; ++cnt; //计数+1 --i; //重新从当前这个点开始遍历 } } if (cur_end_max < t) { cout << "-1" << endl; } else { cout << cnt << endl; } //fclose(stdin); //fclose(stdout); return 0; }
说在最后,这两条题目一条求最大一条求最小,读者们不妨互相对照印证