贪心算法例题讲解(2)

文章首发于:My Blog 欢迎大佬们前来逛逛

P1478 陶陶摘苹果(升级版)

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 提高组] 铺设道路


P5019 [NOIP2018 提高组] 铺设道路

这是一种很重要的贪心问题!

处理连续的一段区间,让这个区间里的数字做一些操作。

常见的这类问题有:

  1. 这段区间的每个值都减少 1,直到为0为止,操作的区间的数字不能是0

  1. 发牌,每次从区间中发出一些牌,只能连续的发,不能跳跃,求出发完所有牌的最小发牌次数

总结为一类问题:求 操作每个元素都不为零 的一段区间,使得整个序列所有的元素都为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 普及组] 纪念品分组

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;
}
posted @ 2023-03-09 23:19  hugeYlh  阅读(26)  评论(0编辑  收藏  举报  来源