有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≤2500,
1≤minSPF≤maxSPF≤1000,
1≤SPF≤1000
输入样例:
3 2
3 10
2 5
1 5
6 2
4 1
输出样例:
2
思路:
minSPF降序排,SPF值降序排。对于每一头牛取能取的最大SPF值防晒霜。这个贪心好理解,严谨的证明可以看这里:https://www.acwing.com/solution/acwing/content/785/
这个过程可以用优先队列优化
证明:当奶牛按minSPF降序排后,对于一头奶牛,假设对应能取的SPF最大防晒霜为 SPF[y],其他能取防晒霜SPF为 SPF[x]。
存在 SPF[y] > SPF[x]。
对于后续奶牛,当SPF[y]不能取的时候,取SPF[y]是自然的。
当SPF[y]能取的时候,一定有SPF[x]能取,那么取SPF[x]和SPF[y]都一样。
而对于第一个奶牛这个决策过程是当前最优的,那么不会有后效性的影响,数学归纳后可以得到后续奶牛选择不需要考虑之前的决策,所以只考虑后续的奶牛和防晒霜条件。所以取SPF[y]更优。
在论坛的时候看到过有人问:
能不能将奶牛的单位强度按照minSPF从小到大排序,然后防晒霜也按照从小到大排序,将最小的先和最小的进行排序呢?
这样貌似和上面的解是对称的,那是对的吗?
是错的。比如(1,5) (2,3) , 和2 ,5防晒霜。
所选择的排列和决策方式,一定也要满足原解的贪心条件,也就是当前所选防晒霜如果能被后续奶牛选,其他防晒霜也必须得能被后续奶牛选,否则会出现选掉后续奶牛特有防晒霜的情况。而当前防晒霜不能被后续奶牛选的时候,选当前防晒霜没问题。
此时只有按maxSPF升序排并且选择最小SPF防晒霜才能满足这样的贪心条件。
这里有两种优先队列的写法,
一种是原解,即降序排列。对于防晒霜可选奶牛,选择当前防晒霜可选奶牛,选择minSPF最大的奶牛。
一种就是刚刚的解,即升序排列。对于防晒霜的可选奶牛,选择maxSPF最小的奶牛。
暴力算法
#include
#include
#include
#include
using namespace std;
const int maxn = 3005;
struct Node
{
int x,y;
}a[maxn],b[maxn];
int cmp(Node aa,Node bb)
{
return aa.x < bb.x;
}
int cmp2(Node aa,Node bb)
{
return aa.x > bb.x;
}
int spf[maxn],cover[maxn];
int main()
{
int c,l;scanf("%d%d",&c,&l);
for(int i = 1;i <= c;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a + 1,a + 1 + c,cmp2);
for(int i = 1;i <= l;i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
}
int ans = 0;
sort(b + 1,b + 1 + l,cmp2);
for(int i = 1;i <= c;i++)
{
for(int j = 1;j <= l;j++)
{
if(b[j].x >= a[i].x && b[j].x <= a[i].y && b[j].y > 0)
{
b[j].y--;ans++;
break;
}
}
}
printf("%d\n",ans);
return 0;
}
优先队列1
#include
#include
#include
#include
using namespace std;
const int maxn = 2550;
struct Node
{
int x,y;
bool operator < (const Node&rhs)const
{
return x < rhs.x;
}
}a[maxn],b[maxn];
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
}
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
}
sort(a + 1,a + 1 + n);
sort(b + 1,b + 1 + m);
priority_queue<int>q;
int cnt = 1,ans = 0;
for(int i = 1;i <= m;i++)
{
while((cnt <= n) && a[cnt].x <= b[i].x)
{
q.push(-a[cnt++].y);
}
while(!q.empty() && -q.top() < b[i].x)
{
q.pop();
}
while(!q.empty() && b[i].y)
{
ans++;q.pop();--b[i].y;
}
}
printf("%d\n",ans);
return 0;
}
优先队列2
#include
#include
#include
#include
using namespace std;
const int maxn = 2550;
struct Node1
{
int x,y;
bool operator < (const Node1&rhs)const
{
return y > rhs.y;
}
}a[maxn];
struct Node2
{
int x,y;
bool operator < (const Node2&rhs)const
{
return x > rhs.x;
}
}b[maxn];
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
}
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
}
sort(a + 1,a + 1 + n);
sort(b + 1,b + 1 + m);
priority_queue<int>q;
int cnt = 1,ans = 0;
for(int i = 1;i <= m;i++)
{
while((cnt <= n) && a[cnt].y >= b[i].x)
{
q.push(a[cnt++].x);
}
while(!q.empty() && q.top() > b[i].x)
{
q.pop();
}
while(!q.empty() && b[i].y)
{
ans++;q.pop();--b[i].y;
}
}
printf("%d\n",ans);
return 0;
}
map迭代器二分写法
#include
#include
#include
#include
#include
using namespace std;
struct Node
{
int x,y;
}a[3005];
int cmp1(Node aa,Node bb)
{
return aa.x > bb.x;
}
map<int,int>mp;
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)scanf("%d%d",&a[i].x,&a[i].y);
for(int i = 1;i <= m;i++)
{
int x,y;scanf("%d%d",&x,&y);
mp[x] += y;
}
sort(a + 1,a + 1 + n,cmp1);
mp[0] = 0;//保证it不为空能够it--
mp[3005] = 0;//保证二分的到
int ans = 0;
map<int,int>::iterator it;
for(int i = 1;i <= n;i++)
{
it = mp.upper_bound(a[i].y);
it--;
if(it -> first >= a[i].x)
{
ans++;
it -> second--;
if(!(it -> second))
{
mp.erase(it);
}
}
}
printf("%d\n",ans);
return 0;
}