贪心算法练习:选择不相交区间

备注:本文纯粹是阅读刘汝佳的《算法竞赛入门经典(第二版)》的一个笔记。笔记文字基本是原文。这里仅仅添加了源代码文件。

问题模型: 数轴上有n个开区间(ai, bi)。 选择尽量多个区间,使得这些区间两两没有公共点。

【分析】
首先明确一个问题:假设有两个区间x,y,区间x完全包含y。 那么,选x是不划算的,因为x和y最多只能选一个,选x还不如选y,这样不仅区间数目不会减少,而且给其他区间留出了更多的位置。 接下来,按照bi从小到大的顺序给区间排序。 贪心策略是:一定要选第一个区间

为什么?

现在区间已经排序成b1≤b2≤b3…了,考虑a1和a2的大小关系。

情况1:a1>a2,如图(a)所示,区间2包含区间1。 前面已经讨论过,这种情况下一定不会选择区间2。 不仅区间2如此,以后所有区间中只要有一个i满足a1>ai,i都不要选。 在今后的讨论中,将不考虑这些区间。

情况2:排除了情况1,一定有a1≤a2≤a3≤…,如图(b)所示。 如果区间2和区间1完全不相交,那么没有影响(因此一定要选区间1),否则区间1和区间2最多只能选一个。 如果不选区间2,黑色部分其实是没有任何影响的(它不会挡住任何一个区间),区间1的有效部分其实变成了灰色部分,它被区间2所包含!由刚才的结论,区间2是不能选的。 依此类推,不能因为选任何区间而放弃区间1,因此选择区间1是明智的。

       

         图(a)a1> a2                      图(b)a1<a2<a3

选择了区间1以后,需要把所有和区间1相交的区间排除在外,需要记录上一个被选择的区间编号。 这样,在排序后只需要扫描一次即可完成贪心过程,得到正确结果

代码如下:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 struct interval
 4 {
 5     int begin,end;
 6     int id;
 7     int flag;//标志区间选择与否以及是否被处理过:0——未处理;1——选择;-1——不选择。 
 8 };
 9 int cmp1(const void *a,const void *b);//按照struct interval 的end从小到大排序所用的比较函数.当end相等,按begin从大到小比较。 
10 int cmp2(const void *a,const void *b);//按id比较两个结构体变量的大小 
11 int main()
12 {
13     int n,i,num=0;
14     int j;
15     struct interval *arr;
16     freopen("5.in","r",stdin);
17     scanf("%d",&n);
18     arr=(struct interval *)malloc(sizeof(struct interval)*n);
19     for(i=0;i<n;i++)
20     {
21         scanf("%d%d",&arr[i].begin,&arr[i].end);
22         arr[i].id=i+1;
23         arr[i].flag=0;
24     }
25     qsort(arr,n,sizeof(struct interval),cmp1);
26     arr[0].flag=1;
27     j=0;//j标示上一次选中的那个区间 
28     num++;
29     for(i=1;i<n;i++)
30     {
31         if(arr[i].begin<arr[j].end)
32         {
33             arr[i].flag=-1;
34         }
35         else
36         {
37             arr[i].flag=1;
38             j=i;
39             num++;
40         }
41     }
42     qsort(arr,n,sizeof(struct interval),cmp2);
43     printf("%d\n",num);
44     for(i=0;i<n;i++)
45     {
46         printf("%-3d %-3d <----> %-3d %-3d\n",arr[i].id,arr[i].begin,arr[i].end,arr[i].flag);
47     }
48     return 0;
49 }
50 int cmp1(const void *a,const void *b)//按照struct interval 的end从小到大排序所用的比较函数.当end相等,按begin从大到小比较。  
51 {
52     int t=(*(struct interval *)a).end - (*(struct interval *)b).end;
53     if(t>0) return 1;
54     else if(t<0) return -1;
55     else
56     {
57         t=(*(struct interval *)b).begin - (*(struct interval *)a).begin;
58         if(t>0) return 1;
59         else if(t<0) return -1;
60         else  return 0;
61     }
62 }
63 int cmp2(const void *a,const void *b)
64 {
65     int t=(*(struct interval *)a).id - (*(struct interval *)b).id;
66     if(t>0) return 1;
67     else if(t<0) return -1;
68     else return 0;
69 }

一个测试样例:

输入

10
-5 -2
-10 -3
1 2
5 10
-3 5
3 20
8 12
11 16
-3 0
0 9

输出

5
1 -5 <----> -2 -1
2 -10 <----> -3 1
3 1 <----> 2 1
4 5 <----> 10 1
5 -3 <----> 5 -1
6 3 <----> 20 -1
7 8 <----> 12 -1
8 11 <----> 16 1
9 -3 <----> 0 1
10 0 <----> 9 -1

猜你喜欢

转载自www.cnblogs.com/huashanqingzhu/p/12745470.html