内容如下:
1)区间完全覆盖问题
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖
样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
解题过程:
1将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]
2设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域
3过程:
假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好达到了8退出,所选区间为3
4贪心证明:
需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖
(2)最大不相交覆盖
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法是不同,这里以开区间为例),问题是从中选取尽量多的线段,使得每个线段都是独立的,就是不和其它有任何线段有相交的地方
样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
解题过程:
对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段
1排序:将每一个区间按右端点进行递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]
2第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2
贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。
(3)区间选点问题
问题描述:给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少选择几个点,使其满足每一条线段的要求.
样例:略
解题过程:将每个线段按照终点坐标进行递增排序,相同终点的前点坐标大的在前面,一个个将其满足
贪心证明:要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能满足后面线段的要求,那么必须是从线段的有端点开始选点,那么问题(2)一样涉及到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足贪心算法的最优子结构性质了。
可以解决的实际问题:数轴上面有n个闭区间[a,b],取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
实例(类似第一种区间覆盖,只不过求最大值)
Input
Output
Sample Input
3 300 1000 700 1200 1500 2100
Sample Output
900 300
参考代码:
- //算法思想:区间覆盖,先排序,总共就两种情况。
- #include<iostream>
- #include<stdio.h>
- #include<fstream>
- #include<algorithm>
- using namespace std;
- #define Max(a,b) a>b?a:b
- typedef struct
- {
- int s_time;
- int e_time;
- }D_farmers;
- D_farmers df[5001];
- int n;
- bool cmp(D_farmers a,D_farmers b)//起始时间的升序排序
- {
- return a.s_time<b.s_time||(a.s_time==b.s_time&&a.e_time<b.e_time);
- }
- int main()
- {
- ifstream fin("milk2.in");
- ofstream fout("milk2.out");
- int i,max1_t=0,max2_t=0,s_max,t_max;//s_max和t_max分别记录最大起始时间和结束时间
- fin>>n;
- for(i=0;i<n;i++)
- fin>>df[i].s_time>>df[i].e_time;
- sort(df,df+n,cmp);//升序排序
- s_max=df[0].s_time;
- t_max=df[0].e_time;
- max1_t=df[0].e_time-df[0].s_time;
- //max2_t=df[0].s_time;
- for(i=1;i<n;i++)
- {
- if(t_max>=df[i].s_time&&df[i].e_time>=t_max)//前一个结束时间大于当前起始时间,更新新的截至时间,
- t_max=df[i].e_time;
- else if(t_max<df[i].s_time)//前一个结束时间小于当前起始时间,进行判断比较
- {
- //cout<<t_max<<endl;
- max1_t=Max(t_max-s_max,max1_t);
- //cout<<max1_t<<endl;
- max2_t=Max(df[i].s_time-t_max,max2_t);
- s_max=df[i].s_time;
- t_max=df[i].e_time;
- }
- }
- //cout<<max1_t<<" "<<max2_t<<endl;
- fout<<max1_t<<" "<<max2_t<<endl;
- return 0;
- }
以下内容复制于http://blog.csdn.net/dgq8211/article/details/7534776
先来看看什么是区间选点问题
数轴上有n个闭区间[ai,bi]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。
贪心策略:
按照b1<=b2<=b3…(b相同时按a从大到小)的方式排序排序,从前向后遍历,当遇到没有加入集合的区间时,选取这个区间的右端点b。
证明:
为了方便起见,如果区间i内已经有一个点被取到,我们称区间i被满足。
1、首先考虑区间包含的情况,当小区间被满足时大区间一定被满足。所以我们应当优先选取小区间中的点,从而使大区间不用考虑。
按照上面的方式排序后,如果出现区间包含的情况,小区间一定在大区间前面。所以此情况下我们会优先选择小区间。
则此情况下,贪心策略是正确的。
2、排除情况1后,一定有a1<=a2<=a3……。
对于区间1来说,显然选择它的右端点是明智的。因为它比前面的点能覆盖更大的范围。
从而此情况下,贪心策略也是正确的。
- #include <stdio.h>
- #include <algorithm>
- using namespace std;
- struct Extent
- {
- int a,b;
- bool operator < (const Extent& S)const
- {
- return b < S.b || b == S.b && a > S.a;
- }
- }A[10002];
- int main()
- {
- int z,n,cnt,end;
- scanf("%d",&z);
- while(z--)
- {
- cnt = 0;
- end = -1;
- scanf("%d",&n);
- for(int i=0;i<n;i++)
- scanf("%d%d",&A[i].a,&A[i].b);
- sort(A,A+n);
- for(int i=0;i<n;i++)
- {
- if(end < A[i].a)
- {
- end = A[i].b;
- cnt++;
- }
- }
- printf("%d\n",cnt);
- }
- return 0;
- }
例题:http://acm.nyist.net/JudgeOnline/problem.php?pid=287
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 38208 | Accepted: 8483 |
Description
We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates.
Figure A Sample Input of Radar Installations
Input
The input is terminated by a line containing pair of zeros
Output
Sample Input
3 2 1 2 -3 1 2 11 2 0 20 0
Sample Output
Case 1: 2 Case 2: 1
Source
题目的意思就是给你一个坐标轴,雷达在x轴上,岛屿分布在x轴上方,给你岛屿的坐标以及雷达的最大扫描面积,求最少用几个雷达可以将所有的岛屿覆盖!
思路:
以岛为圆心,以d为半径画圆(d是雷达的辐射半径),其与x轴相交的区间为一个区 这样就变成了在区间内找最少的点问题了
- #include<iostream>
- #include<cmath>
- using namespace std;
- typedef struct
- {
- double l,r;
- }in;
- int cmp(const void *a, const void *b)
- {
- return (*(in *)a).l >= (*(in *)b).l ? 1:-1;
- }
- int main()
- {
- int n,d,i,x,y,sw,re,count = 1;
- double pre;
- in p[1000];
- while(1)
- {
- cin>>n>>d;
- if(n == 0 && d==0) break;
- sw = 1;
- for(i=0;i<n;i++)
- {
- cin>>x>>y;
- if(d>=y&&sw==1)
- {
- p[i].l = x-sqrt((double)d*d - (double)y*y);
- p[i].r = x+sqrt((double)d*d - (double)y*y);
- }
- else
- {
- sw = 0;
- }
- }
- if(sw == 0)
- {
- cout<<"Case "<<count++<<": "<<-1<<endl;
- continue;
- }
- qsort(p,n,sizeof(in),cmp);
- re = 1;
- pre = p[0].r;
- for(i=1;i<n;i++)
- {
- if(p[i].l>pre)
- {
- re++;
- pre = p[i].r;
- }
- else
- {
- if(p[i].r<pre)
- {
- pre = p[i].r;
- }
- }
- }
- cout<<"Case "<<count++<<": "<<re<<endl;
- }
- return 0;
- }
区间均取最少2个点的问题:
给定一个大区间[a,b],再给出几个小区间 ,找出满足下述条件的所含元素个数最少的集合的个数,该集合满足
——对于给定的每一个区间,都至少有两个不同的整数属于该集合。
本题数据规模较大,用搜索做会超时,而动态规划无从下手。考虑贪心算法。题目意思是要找一个集合,该集合中的数的个数既要少又要和所给定的所有区间都有2个点的交集。(每个区间至少有两个该集合中的数)。我们可以从所给的区间中选数,为了选尽量少的数,应该使所选的数和更多的区间有交集这就是贪心的标准。一开始将所有区间按照右端点从小到大排序。从第一个区间开始逐个向后检查,看所选出的数与所查看的区间有无交集,有两个则跳过,只有一个数相交,就从当前区间中选出最大的一个数(即右端点),若无交集,则从当前区间选出两个数,就(右端点,右端点-1),直至最后一个区间。
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- struct prince
- {
- int left,right;//区间左右端点
- }a[10000];
- int n;
- int result;//存放结果中的数
- int cmp(const void *a,const void *b)
- {
- return (*(prince *)a).right-(*(prince *)b).right;
- }
- int work()
- {
- qsort(a+1,n,sizeof(a[0]),cmp);//按区间右端点由小到大排序
- int i;
- int a1,a2;
- a1=a[1].right-1;a2=a[1].right;result=2;
- for(i=2;i<=n;i++)
- {
- if(a[i].left<=a1&& a[i].right>=a2)continue;//完全包含的情况下 其区间的2点就是上次的2点 直接跳过
- if (a[i].left>a2 )//完全不包含 则要添加进去2点
- {
- a1=a[i].right-1;a2=a[i].right;result=result+2;
- }
- if (a[i].left>a1 && a[i].right>a2 && a[i].left<=a2)
- {
- a1=a2;a2=a[i].right;result++;}//只包含一个
- }
- return result;
- }
- int main()
- {
- scanf("%d",&n);
- int i;
- for(i=1;i<=n;i++)
- scanf("%d %d",&a[i].left,&a[i].right);
- printf("%d\n",work());
- return 0;
- }
Problem 1230 区间相交问题
http://acm.fzu.edu.cn/problem.php?pid=1230
Problem Description
★算法设计:对于给定的 n 个闭区间,计算去掉的最少闭区间数。
Input
Output
Sample Input
Sample Output
Source
思路,贪心,按右端点排序,然后从小到大选,第一个肯定要选,区间相交的情况分两种,一种是一个区间被另一个区间所包含,那么选那个区间比较小的那个,为其他区间腾出区间,另外就是不包含的话就以当前区间右端点为基准,直到有区间的左顶点大于它为止,更新当前区间右端点
- #include <iostream>
- #include <cstdio>
- #include <algorithm>
- using namespace std;
- const int maxn=40002;
- struct segment
- {
- int begin, end;
- segment(int _b=0, int _e=0):begin(_b),end(_e){};
- inline bool operator<( const segment& ss ) const
- {//按照区间的右端点排序
- return end<ss.end || (end==ss.end)&&(begin<ss.begin);
- }
- inline void input()
- {
- scanf("%d %d",&begin, &end);
- if(begin>end)//保证左端点值不比右端点大
- begin^=end, end^=begin, begin^=end;
- }
- }seg[maxn];
- int main()
- {
- int n;
- while(scanf("%d",&n)!=EOF)
- {
- int i, res=1, limit;
- for(i=0; i<n; i++)
- seg[i].input();
- sort(seg,seg+n);
- limit=seg[0].end;
- for(i=1; i<n; i++)
- {//seg[i].begin<=limit的所有区间都是相互相交的,因为这些区间必然有公共点limit,即某一个区间的右端点
- if(seg[i].begin>limit)
- res++, limit=seg[i].end;
- }
- printf("%d\n",n-res);
- }
- return 0;
- }
Bus | ||
Accepted : 63 | Submit : 514 | |
Time Limit : 1000 MS | Memory Limit : 65536 KB |
题目描述
小强刚来到长沙这个大城市,发现这里有很多他老家没有的东西,其中一个就是公交车了。小强的家到学校有很多个公交站,每个公交站都有一个英文名字。小强很喜欢坐公交车,但是他有个奇怪的要求,就是公交车的上车站和下车站的英文名字必须是首字母相同的,且不在同一个站上下车,不然小强宁愿走过这个站去搭下一趟车,甚至直接走到学校。给出小强从家里到学校的之间每一个公交站的英文名字,问如果不往回走,小强最多能搭几次公交车?
输入
多组样例,每组样例第一行为非负整数n(n<=1000),表示小强家里到学校之间有n个公交站。接下来n行,每行有一个英文名字,每行不超过100字符。
输出
对于每组样例,输出最多的乘坐次数。
样例输入
4 shaoshan erzhong shangxia dongmen 5 shaoshan shangxia ertian erzhong dongmen
样例输出
1 2
http://202.197.224.59/OnlineJudge2/index.php/Contest/read_problem/cid/24/pid/1151
和上面那个题一样
思路:按照题意,可以将首字母相同的站当成一个区间。题目就转换成了,从x个区间中,怎样选择让不想交的区间最多。思路是贪心。
代码:
- #include <stdio.h>
- #include <algorithm>
- using namespace std;
- struct st
- {
- int s,e;
- }a[1000002];
- char ch[1002][103];
- bool cmp(st a,st b)
- {
- if(a.e==b.e)
- return a.s<b.s;
- return a.e<b.e;
- }
- int main()
- {
- int i,j,n;
- int tm;
- int w;
- while(scanf("%d",&n)!=EOF)
- {
- tm=0;
- int s;
- for(i=0;i<n;i++)
- {
- scanf("%s",ch[i]);
- for(j=0;j<i;j++)
- {
- if(ch[i][0]==ch[j][0])
- {
- a[tm].s=j;
- a[tm++].e=i;
- }
- }
- }
- if(tm>0)
- {
- s=1;
- w=a[0].e;
- for(i=1;i<tm;i++)
- {
- if(a[i].s>w)
- {
- s++;
- w=a[i].e;
- }
- }
- printf("%d\n",s);
- }
- else
- printf("0\n");
- }
- return 0;
- }
以上内容转自:https://blog.csdn.net/enjoying_science/article/details/41177531
基于贪心算法的几类区间覆盖问题:
(1)区间完全覆盖问题
问题描述:
给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),
求最少使用多少条线段可以将整个区间完全覆盖
样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
解题过程:
1、将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],
[3,6],[3,7],[6,8]
2、设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前
已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域
3、过程:
假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],
由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好
达到了8退出,所选区间为3
4、贪心证明:
需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前
的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),
那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖
(2)最大不相交覆盖(我总感觉这个算法不对,这不应该和会议安排问题一样吗? 直接按照终点排序再依次选择???)
问题描述:
给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法是
不同,这里以开区间为例),问题是从中选取尽量多的线段,使得每个线段都是独立的,
就是不和其它有任何线段有相交的地方
样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
解题过程:
对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个
(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会
跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段
1、排序:将每一个区间按右端点进行递增顺序排列,拍完序后为[1,4],[2,4],[3,5],[2,6],
[3,6],[3,7],[6,8]
2、第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2
3、贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,
对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?
如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有
的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。
(3)区间选点问题
问题描述:
给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求
(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少
选择几个点,使其满足每一条线段的要求.
样例:略
解题过程:
将每个线段按照终点坐标进行递增排序,相同终点的前点坐标从大到小排列,
一个个将其满足(每次选择的点为该条线段的右端点)
贪心证明:
要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能
在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能
满足后面线段的要求,那么必须是从线段的右端点开始选点,那么问题(2)一样涉及
到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,
每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足
贪心算法的最优子结构性质了。
可以解决的实际问题:数轴上面有n个闭区间[a,b],取尽量少的点,使得每个区间内都
至少有一个点(不同区间内含的点可以是同一个)
应用例题:(貌似不是很简单。。。)
有一列整数,他的每一个数各不相同,我们不知道有多少个,但我们知道在
某些区间中至少有多少个整数,用区间(L,R,C)来描述,表示整数序列
中至少有C个整数来自子区间[L, R],若干个这样的区间,问这个整数序列的长
度最少能为多少。
区间选点算法实现:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 struct line 7 { 8 int left; 9 int right; 10 }a[100]; 11 12 bool cmp(line p, line q) 13 { 14 if(p.right != q.right) 15 return p.right < q.right; 16 return p.left > q.left; 17 } 18 19 int main() 20 { 21 int n; 22 while(cin >> n) 23 { 24 for(int i = 0; i < n; ++i) 25 cin >> a[i].left >> a[i].right; 26 sort(a, a + n, cmp); 27 int cnt = 0; 28 int end = -1; 29 for(int i = 0; i < n; ++i) 30 { 31 if(end >= a[i].left && end <= a[i].right) 32 continue; 33 else 34 { 35 ++cnt; 36 end = a[i].right; 37 } 38 } 39 cout << cnt << endl; 40 } 41 return 0; 42 }
以上内容转自:https://www.cnblogs.com/dongsheng/archive/2013/04/19/3030444.html