备注:本文纯粹是阅读刘汝佳的《算法竞赛入门经典(第二版)》的一个笔记。笔记文字基本是原文。这里仅仅添加了源代码文件。
问题模型: 数轴上有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