众所周知,贪心是一个比较恶心的算法。
区别于常规的算法或数据结构,贪心题一般不会让人看到就想到思路,而是需要我们在一些猜测或是感性分析下,找到一种局部最优的方案,并且可以通过局部最优解推出全局最优解。
T1:防晒
有C头奶牛进行日光浴,第i头奶牛需要minSPF[i]到maxSPF[i]单位强度之间的阳光。
每头奶牛在日光浴前必须涂防晒霜,防晒霜有L种,涂上第i种之后,身体接收到的阳光强度就会稳定为SPF[i],第i种防晒霜有cover[i]瓶。
求最多可以满足多少头奶牛进行日光浴。
输入格式
第一行输入整数C和L。
接下来的C行,按次序每行输入一头牛的minSPF和maxSPF值,即第i行输入minSPF[i]和maxSPF[i]。
再接下来的L行,按次序每行输入一种防晒霜的SPF和cover值,即第i行输入SPF[i]和cover[i]。
每行的数据之间用空格隔开。
输出格式
输出一个整数,代表最多可以满足奶牛日光浴的奶牛数目。
数据范围
1≤C,L≤25001≤C,L≤2500,
1≤minSPF≤maxSPF≤10001≤minSPF≤maxSPF≤1000,
1≤SPF≤10001≤SPF≤1000
输入样例:
3 2
3 10
2 5
1 5
6 2
4 1
输出样例:
2
不难想到,该题的贪心策略为:
1.将所有奶牛的maxSPF从大到小排序。
2.枚举每一头奶牛,选择可选防晒霜中SPF值最大的一个使用。
证明:
1.如果把所有奶牛与防晒霜的SPF值都看成点,那么这个题实际上就是一个二分图的最大匹配问题。
2.根据匈牙利算法,如果已匹配的二分图中不存在增广路,那么这就是一个最大匹配的二分图。
3.如果以我们的算法不可以找到最短增广路,那么此算法即为正解4.如果以一个防晒霜a的SPF值为起点,那么根据算法可以看出,下一个增广路径中的防晒霜的SPF值b一定在a所匹配的奶牛的SPF区间的右侧。
5.那么就可以根据增广路径的最短性得知增广路径只能从奶牛maxSPF由小到大去得到。
6.如果我们找到了最短增广路,那么此时具有最大maxSPF的奶牛没有点匹配,而具有第二大maxSPF的奶牛有点匹配,与算法矛盾。7.该算法正确。
代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=2510; typedef pair<int,int> PII; PII cow[N],cover[N]; int C,L; int main() { cin>>C>>L; for (int i=0;i<C;++i) cin>>cow[i].first>>cow[i].second; for (int i=0;i<L;++i) cin>>cover[i].first>>cover[i].second; sort(cow,cow+C); int ans=0; for (int i=C-1;i>=0;--i) { int max_cover=0,p; for (int j=0;j<L;++j) if (cover[j].first>=cow[i].first&&cover[j].first<=cow[i].second&&max_cover<cover[j].first) max_cover=cover[j].first,p=j; if (max_cover!=0) { ans++; cover[p].second--; if (cover[p].second==0) cover[p].first=0; } } cout<<ans; return 0; }
T2.畜栏预定
有N头牛在畜栏中吃草。
每个畜栏在同一时间段只能提供给一头牛吃草,所以可能会需要多个畜栏。
给定N头牛和每头牛开始吃草的时间A以及结束吃草的时间B,每头牛在[A,B]这一时间段内都会一直吃草。
当两头牛的吃草区间存在交集时(包括端点),这两头牛不能被安排在同一个畜栏吃草。
求需要的最小畜栏数目和每头牛对应的畜栏方案。
输入格式
第1行:输入一个整数N。
第2..N+1行:第i+1行输入第i头牛的开始吃草时间A以及结束吃草时间B,数之间用空格隔开。
输出格式
第1行:输入一个整数,代表所需最小畜栏数。
第2..N+1行:第i+1行输入第i头牛被安排到的畜栏编号,编号从1开始,只要方案合法即可。
数据范围
1≤N≤500001≤N≤50000,
1≤A,B≤10000001≤A,B≤1000000
输入样例:
5
1 10
2 4
3 6
5 8
4 7
输出样例:
4
1
2
3
2
4
更不难想到,此题的贪心做法:
1.将每头牛吃草的起始时间A排序。
2.枚举每一头牛,若此时所有畜栏的最小吃草末尾时间<该牛的起始时间,就另增一个畜栏,并保存末尾时间。
证明:
1.根据此算法得出的方案中的所有畜栏不存在任意两个之间没有交集。
2.所以最优解>=求得的方案。
3.又因为最优解<=求得的方案。
4.该算法正确。
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<vector> using namespace std; const int N=50010; typedef pair<int,int> PII; pair<PII,int> cow[N]; int n,mark[N]; int main() { cin>>n; for (int i=0;i<n;++i) { cin>>cow[i].first.first>>cow[i].first.second; cow[i].second=i; } sort(cow,cow+n); priority_queue< PII,vector<PII>,greater<PII> > heap; for (int i=0;i<n;++i) if (heap.empty()||cow[i].first.first<=heap.top().first) { PII stall=make_pair(cow[i].first.second,heap.size()+1); heap.push(stall); mark[cow[i].second]=stall.second; } else { mark[cow[i].second]=heap.top().second; PII stall=make_pair(cow[i].first.second,heap.top().second); heap.pop(); heap.push(stall); } cout<<heap.size()<<endl; for (int i=0;i<n;++i) cout<<mark[i]<<endl; return 0; }
T3.雷达设备
假设海岸是一条无限长的直线,陆地位于海岸的一侧,海洋位于另外一侧。
每个小岛都位于海洋一侧的某个点上。
雷达装置均位于海岸线上,且雷达的监测范围为d,当小岛与某雷达的距离不超过d时,该小岛可以被雷达覆盖。
我们使用笛卡尔坐标系,定义海岸线为x轴,海的一侧在x轴上方,陆地一侧在x轴下方。
现在给出每个小岛的具体坐标以及雷达的检测范围,请你求出能够使所有小岛都被雷达覆盖所需的最小雷达数目。
输入格式
第一行输入两个整数n和d,分别代表小岛数目和雷达检测范围。
接下来n行,每行输入两个整数,分别代表小岛的x,y轴坐标。
同一行数据之间用空格隔开。
输出格式
输出一个整数,代表所需的最小雷达数目,若没有解决方案则所需数目输出“-1”。
数据范围
1≤n≤10001≤n≤1000
输入样例:
3 2
1 2
-3 1
2 1
输出样例:
2
算法:
把所有岛屿的x轴上可监测区间求出。
把所有区间的末端点排序。
枚举每一个区间,若第i'区间初端点被第i区间末端点包含,方案数+1,并保存新的末端点。
证明:
该算法得出的每个方案中的首区间两两不存在交集,因此得证。
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef pair<double,double> PII; const int N=1010; PII len[N]; bool cmp(PII x,PII y) { return x.second<y.second; } int main() { int n; double d; cin>>n>>d; for (int i=0;i<n;++i) { cin>>len[i].first>>len[i].second; int x=len[i].first,y=len[i].second; if (y>d) {cout<<-1; return 0;} len[i]=make_pair(x-sqrt(d*d-y*y),x+sqrt(d*d-y*y)); } sort(len,len+n,cmp); int ans=1; double p=len[0].second; for (int i=1;i<n;++i) if (len[i].first>p) ++ans,p=len[i].second; cout<<ans; }