一些贪心题的题解

众所周知,贪心是一个比较恶心的算法。

区别于常规的算法或数据结构,贪心题一般不会让人看到就想到思路,而是需要我们在一些猜测或是感性分析下,找到一种局部最优的方案,并且可以通过局部最优解推出全局最优解。

 

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;
}

 

 
上一篇:POJ 3617 Best Cow Line 贪心+模拟


下一篇:Greedy Pie Eaters(区间DP)