贪心算法——区间选点问题

曾经终败给现在 2022-05-09 07:08 268阅读 0赞

转载:https://blog.csdn.net/xia842655187/article/details/51944763

区间选点的问题大致可以描述为:
给定N个区间[a,b],取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以重复)。

关于贪心算法的验证过程就不再赘述,现在思考一下贪心策略的制定。
对于区间[a1, b1] 、[a2, b2]、 [a3, b3] 来说,
如果想选择最少的点,那么必须选择每个区间的右端点,示意图如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM4ODE2OTI0_size_16_color_FFFFFF_t_70

当你每一次都选择区间的最右端,才能保证每一个选的点覆盖的范围都是最广泛的,也就是说选的点才是最少的。

和之前不相交区间的思考方法类似,把区间进行预处理,按照端点的大小排序(同样,按照右端点排序会好理解一点,但是左端点排序一样可以起到作用,初学者不必迷信右端点排序)。
预处理过后,求解策略的思路和求不相交区间相似,如果下一个区间的左端点不被覆盖,则答案+1,如下:

  1. while(剩余区间的数目不为0)
  2. {
  3. if(找到符合条件的下一个区间)
  4. {
  5. 当前区间 = 下一个区间;
  6. 答案数+1
  7. }
  8. 区间数--;
  9. }

代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. const int maxn=20;
  5. struct timetable
  6. {
  7. int a;
  8. int b;
  9. }time[maxn];
  10. bool comp(timetable &x,timetable &y)//多看两遍
  11. {
  12. if(x.b!=y.b)
  13. return (x.b<y.b);
  14. return (x.a<y.a);
  15. }
  16. int main()
  17. {
  18. int i,n,count;
  19. while(cin>>n)
  20. {
  21. count=1;
  22. for(i=0;i<n;i++)//输入
  23. {
  24. cin>>time[i].a;
  25. cin>>time[i].b;
  26. }
  27. sort(time,time+n,comp);//按b从小到大排序
  28. int newend=time[0].b;
  29. cout<<"("<<time[0].a<<","<<time[0].b<<")"<<" "; //第一个必然是
  30. for(i=1;i<n;i++)
  31. {
  32. if(newend<time[i].a)
  33. {
  34. count++;
  35. newend=time[i].b;
  36. cout<<"("<<time[i].a<<","<<time[i].b<<")"<<" ";
  37. }
  38. }
  39. cout<<endl<<count<<endl;
  40. }
  41. return 0;
  42. }

题目描述
家住非洲的小孩,都很黑。为什么呢?
第一,他们地处热带,太阳辐射严重。
第二,他们不经常洗澡。(常年缺水,怎么洗澡。)
现在,在一个非洲部落里,他们只有一个地方洗澡,并且,洗澡时间很短,瞬间有木有!!(这也是没有的办法,缺水啊!!)
每个小孩有一个时间段能够洗澡。并且,他们是可以一起洗的(不管你是男孩是女孩)。
那么,什么时间洗澡,谁应该来洗,由谁决定的呢?那必然是他们伟大的“澡”神啊。“澡”神有一个时间表,记录着该部落的小孩,什么时候段可以洗澡。现在,“澡”神要问你,一天内,他需要最少开启和关闭多少次洗澡的水龙头呢?因为,开启和关闭一次水龙头是非常的费力气的,即便,这也是瞬间完成的。

输入
多组数据
第一行一个n<=100。
接下来n行,每行一个时间段。H1H1:M1M1-H2H2:M2M2,24小时制。
保证该时间段是在一天之内的。但是,不保证,H1H1:M1M1先于H2H2:M2M2。

输出
题目描述,“澡”神最少需要开启和关闭多少次水龙头呢?

样例输入
1
00:12-12:12
2
00:12-12:12
14:00-12:00

样例输出
1
1

提示
Ps:开启和关闭为一次


这道题是完美的区间选点,但是数据有坑(不保证H1H1:M1M1先于H2H2:M2M2。) 所以读入的时候要注意进行判断。

解决代码如下:

  1. /*
  2. ************************************
  3. Title: NYOJ1036-非洲小孩
  4. ************************************
  5. Date:2015/07/23
  6. ************************************
  7. author:刘旭
  8. ************************************
  9. Memory:256KB
  10. Time:8ms
  11. ************************************
  12. */
  13. #include <iostream>
  14. #include <cstdio>
  15. #include <cstring>
  16. #include <algorithm>
  17. using namespace std;
  18. #define MAX 1005
  19. struct Node
  20. {
  21. int x;
  22. int y;
  23. };
  24. Node map[MAX];
  25. bool cmp(Node a,Node b){
  26. if(a.y != b.y){
  27. return a.y < b.y;
  28. }
  29. return a.x < b.x;
  30. }
  31. int main()
  32. {
  33. int num = 0;
  34. while(EOF != scanf("%d", &num)){
  35. memset(map, -1, sizeof(map));
  36. int a1,b1,a2,b2;
  37. for(int i = 0; i < num; i++){
  38. scanf("%d:%d-%d:%d",&a1,&b1,&a2,&b2);
  39. int key1 = a1*60+b1;
  40. int key2 = a2*60+b2;
  41. if(key1 > key2){
  42. swap(key1, key2);
  43. }
  44. map[i].x = key1;
  45. map[i].y = key2;
  46. }
  47. sort(map, map+num, cmp);
  48. int start = map[0].y;
  49. int num_node = 0;
  50. int ans = 1;
  51. while(num - num_node){
  52. if(map[num_node].x > start){
  53. start = map[num_node].y;
  54. ans++;
  55. }
  56. num_node++;
  57. }
  58. printf("%d\n", ans);
  59. }
  60. return 0;
  61. }

发表评论

表情:
评论列表 (有 0 条评论,268人围观)

还没有评论,来说两句吧...

相关阅读