贪心算法例题讲解(2)
文章首发于:My Blog 欢迎大佬们前来逛逛
P1478 陶陶摘苹果(升级版)
直接将摘苹果所需要的力气按照从到大排序即可。
那么这样,摘的最轻松的苹果不摘白不摘,摘它这么轻松,干嘛不摘。
如果遇到了 足够力气摘得苹果,但是高度够不到,不摘不就好了,又不损失什么,反正我们是按照花费得力气排序的。
//TODO: Write code here
int n,m;
int a,b;
const int N=1e5+10;
int nums[N];
struct Node
{
int x,y;
}apple[N];
bool comp(Node a,Node b)
{
if (a.y<b.y) return true;
return false;
}
signed main()
{
cin>>n>>m>>a>>b;
for (int i=1;i<=n;i++)
{
cin>>apple[i].x>>apple[i].y;
}
sort(apple+1,apple+1+n,comp);
int sum=a+b;
int ans=0;
for (int i=1;i<=n;i++)
{
if (apple[i].x<=sum && apple[i].y<=m)
{
m-=apple[i].y;
ans+=1;
}
}
cout<<ans;
#define one 1
return 0;
}
P5019 [NOIP2018 提高组] 铺设道路
这是一种很重要的贪心问题!
处理连续的一段区间,让这个区间里的数字做一些操作。
常见的这类问题有:
这段区间的每个值都减少 1,直到为0为止,操作的区间的数字不能是0
发牌,每次从区间中发出一些牌,只能连续的发,不能跳跃,求出发完所有牌的最小发牌次数
总结为一类问题:求 操作每个元素都不为零 的一段区间,使得整个序列所有的元素都为0的最少操作次数,每次操作只能操作一段区间
回到这题:贪心算法如何求解?
可以注意到如果第 i 个的元素大于第 i-1 个位置的元素,则对 i 和 i-1 位置的操作次数一定是 nums[i] 减去 nums[i-1]
即:
if (nums[i]>nums[i-1]) ans+=nums[i]-nums[i-1]
注意到 : i 位置的元素一定比 i-1 位置的元素大,因此无论如何你一定要 对 i 位置的元素操作为0,所以对 i 位置的元素操作相当于免费操作了 i -1 位置的元素。
类似于如下:
4 3 2 5 3 5
4 3 0 2 3 5
4 3 0 2 1 3
最后遍历完整个序列之后,ans+ =nums[1]
0 0 0 0 0
//TODO: Write code here
int n,m;
const int N=1e5+10;
int nums[N];
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>nums[i];
}
int ans=0;
for (int i=1;i<n;i++)
{
if (nums[i]<nums[i+1])
{
ans+=nums[i+1]-nums[i];
}
}
cout<<ans+nums[1];
#define one 1
return 0;
}
P1208 [USACO1.3]混合牛奶 Mixing Milk
P1208 [USACO1.3]混合牛奶 Mixing Milk
贪心思路:直接按照牛奶的单价进行排序,如果单价有一样的,则按照 量多的在前面
很明显,量多的并且单价还便宜的一定是最优的。
因此直接AC:
//TODO: Write code here
int n,m;
const int N=2e6+10;
int nums[N];
struct Node
{
int a,p;
}node[N];
bool comp(Node a,Node b)
{
if (a.a<b.a) return true;
else if (a.a==b.a) return a.p>b.p;
return false;
}
signed main()
{
cin>>n>>m;
for (int i=1;i<=m;i++)
{
cin>>node[i].a>>node[i].p;
}
sort(node+1,node+1+m,comp);
int ans=0;
for (int i=1;i<=m;i++)
{
if (n<=0) break;
if (node[i].p<=n)
{
n-=node[i].p;//购买了p个
ans+=node[i].a*node[i].p;//p种的总成本
}
else
{
ans+=node[i].a*n;
break;
}
}
cout<<ans;
#define one 1
return 0;
}
P1094 [NOIP2007 普及组] 纪念品分组
很典型的贪心问题:要使得组的数量尽可能少,则组内两个纪念品一定是一个尽量可能小,一个尽量可能大
这样才满足一个组的利用率达到最高
即寻找 小于等于最大价值之和的 能够提供最大价值的两个纪念品
则我们单价进行排序,左边尽量小 和 右边尽量大 的在 满足不大于最大价值限制的情况下,要组成一组,
如果:
if (s左 + s右 > limit)
{
I右--
}
else
{
I左++
I右--
}
则这样遍历完一遍后,整个序列中满足条件的两个最优的纪念品就分好了,则其他的就只能单独一组!
贪心算法的证明, 某大佬的解答:
题解 P1094 【纪念品分组】 - heidoudou 的博客 - 洛谷博客 (luogu.com.cn)
//TODO: Write code here
int n,m;
const int N=1e5+10;
int nums[N];
bool fg[N];
signed main()
{
cin>>m>>n;
for (int i=1;i<=n;i++)
{
cin>>nums[i];
}
sort(nums+1,nums+1+n);
int l=1,r=n;
int ans=0;
while (l<r)
{
if (nums[l]+nums[r]>m)
{
//不能分组
r--;
}
else
{
ans++;
fg[l]=true,fg[r]=true;
r--;
l++;
}
//dbg(fg,n);
}
for (int i=1;i<=n;i++)
{
if (fg[i]==false)
{
ans+=1;
}
}
cout<<ans;
#define one 1
return 0;
}
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209591.html