文章首发于:My Blog 欢迎大佬们前来逛逛
P1478 陶陶摘苹果(升级版) P1478 陶陶摘苹果(升级版)
直接将摘苹果所需要的力气按照从到大排序即可。
那么这样,摘的最轻松的苹果不摘白不摘 ,摘它这么轻松,干嘛不摘。
如果遇到了 足够力气摘得苹果,但是高度够不到,不摘不就好了 ,又不损失什么,反正我们是按照花费得力气排序的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 提高组] 铺设道路
P5019 [NOIP2018 提高组] 铺设道路
这是一种很重要的贪心问题!
处理连续的一段区间,让这个区间里的数字做一些操作。
常见的这类问题有:
这段区间的每个值都减少 1,直到为0为止,操作的区间的数字不能是0
发牌,每次从区间中发出一些牌,只能连续的发,不能跳跃,求出发完所有牌的最小发牌次数
总结为一类问题:求 操作每个元素都不为零 的一段区间,使得整个序列所有的元素都为0的最少操作次数,每次操作只能操作一段区间
回到这题:贪心算法如何求解?
可以注意到如果第 i 个的元素大于第 i-1 个位置的元素,则对 i 和 i-1 位置的操作次数一定是 nums[i] 减去 nums[i-1]
即:
1 if (nums[i]>nums[i-1 ]) ans+=nums[i]-nums[i-1 ]
注意到 : i 位置的元素一定比 i-1 位置的元素大,因此无论如何你一定要 对 i 位置的元素操作为0,所以对 i 位置的元素操作相当于免费操作了 i -1 位置的元素。
类似于如下:
最后遍历完整个序列之后,ans+ =nums[1]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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; ans+=node[i].a*node[i].p; } else { ans+=node[i].a*n; break ; } } cout<<ans; #define one 1 return 0 ; }
P1094 [NOIP2007 普及组] 纪念品分组 P1094 [NOIP2007 普及组] 纪念品分组
很典型的贪心问题:要使得组的数量尽可能少,则组内两个纪念品一定是一个尽量可能小,一个尽量可能大
这样才满足一个组的利用率达到最高
即寻找 小于等于最大价值之和的 能够提供最大价值的两个纪念品
则我们单价 进行排序,左边尽量小 和 右边尽量大 的在 满足不大于最大价值限制 的情况下,要组成一组,
如果:
1 2 3 4 5 6 7 8 9 if (s左 + s右 > limit) { I右-- } else { I左++ I右-- }
则这样遍历完一遍后,整个序列中满足条件的两个最优的纪念品就分好了 ,则其他的就只能单独一组!
贪心算法的证明, 某大佬的解答:
题解 P1094 【纪念品分组】 - heidoudou 的博客 - 洛谷博客 (luogu.com.cn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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++; } } for (int i=1 ;i<=n;i++) { if (fg[i]==false ) { ans+=1 ; } } cout<<ans; #define one 1 return 0 ; }