第六章 贪心 【完结】

贪心类题型没有固定的模板只有多做题,总结经验。
基本都熟练了

905. 区间选点

在这里插入图片描述
https://www.acwing.com/problem/content/description/907/

本题和区间合并那种题几乎一样的思路,不过还是有差别的。
首先:按左端点从小到达排,这是没有悬念的必须做的。
这时候分析,会有如下几种情况:
情况一:
在这里插入图片描述
这种包含的情况,我们要选小的,因为我们要的是公共部分。
情况二:
在这里插入图片描述
这种相交的情况选,相交的。
情况三:
在这里插入图片描述
不相交的情况选b,个数加1。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct student 
{
    int x,y;
}stu[N];
bool cmp(student a,student b)
{
    return a.x<b.x;
}
int main(void)
{
    int n; cin>>n;
    
    for(int i=0;i<n;i++) cin>>stu[i].x>>stu[i].y;
    
    sort(stu,stu+n,cmp);
    
    int ans=1;
    int startx=stu[0].x,endx=stu[0].y;
    for(int i=1;i<n;i++)
    {
        if(stu[i].x<=endx&&stu[i].y<=endx) startx=stu[i].x,endx=stu[i].y;
        else if(stu[i].x<=endx&&stu[i].y>endx) startx=stu[i].x;
        else ans++,startx=stu[i].x,endx=stu[i].y;
    }
    cout<<ans<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
vector<PII> ve;
int main(void)
{
    int n; cin>>n;
    while(n--)
    {
        int l,r; cin>>l>>r;
        ve.push_back({l,r});
    }
    sort(ve.begin(),ve.end());
    int ans=0;
    int l=-1e9-10,r=-1e9-5;
    for(int i=0;i<ve.size();i++)
    {
        if(ve[i].first>r) l=ve[i].first,r=ve[i].second,ans++;
        else if(ve[i].second<=r) l=ve[i].first,r=ve[i].second;
        else if(ve[i].first<=r&&ve[i].second>=r) l=ve[i].first;
    }
    cout<<ans<<endl;
    return 0;
}

方法二 从小到大排序右端点:
在这里插入图片描述
因为我们选右端点,才可能尽量大的覆盖掉后面的区间

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1e5+10;
struct node
{
    int x,y;
}Node[N];
bool cmp(node a,node b)
{
    return a.y<b.y;
}
int main(void)
{
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>Node[i].x>>Node[i].y;
    sort(Node,Node+n,cmp);
    int ans=0;
    int flag=-1e9;
    for(int i=0;i<n;i++)
    {
        if(flag<Node[i].x) ans++,flag=Node[i].y;
    }
    cout<<ans<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
vector<PII> ve;
int main(void)
{
    int n; cin>>n;
    while(n--)
    {
        int l,r; cin>>l>>r;
        ve.push_back({r,l});
    }
    int flag=-1e9;
    int ans=0;
    sort(ve.begin(),ve.end());
    for(int i=0;i<ve.size();i++) if(flag<ve[i].second) ans++,flag=ve[i].first;
    cout<<ans<<endl;
    return 0;
}

908. 最大不相交区间数量

在这里插入图片描述
https://www.acwing.com/problem/content/910/

老规矩:左端点从小到大排序
第一种情况:
在这里插入图片描述
这种得选a,这样右边的区间的选择范围才会更大。
第二种情况:
在这里插入图片描述
此时不用变,还是a这样才会有更大的空间。
第三种情况:
在这里插入图片描述
选b,数量加1

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct student
{
    int x,y;
}stu[N];
bool cmp(student a,student b)
{
    return a.x<b.x;
}
int main(void)
{
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>stu[i].x>>stu[i].y;
    sort(stu,stu+n,cmp);
    
    int ans=1;
    int startx=stu[0].x,endx=stu[0].y;
    
    for(int i=1;i<n;i++)
    {
        if(stu[i].y<endx) startx=stu[i].x,endx=stu[i].y;
        else if(stu[i].x>endx) ans++,startx=stu[i].x,endx=stu[i].y;
    }
    
    cout<<ans<<endl;
    return 0;
}

方法二:从小到大排序右端点

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct node
{
    int x,y;
}Node[N];
bool cmp(node a,node b)
{
    return a.y<b.y;
}
int main(void)
{
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>Node[i].x>>Node[i].y;
    sort(Node,Node+n,cmp);
    int ans=0;
    int flag=-1e9;
    for(int i=0;i<n;i++)
    {
        if(Node[i].x>flag) ans++,flag=Node[i].y;
    }
    cout<<ans<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
vector< pair<int,int> > ve;
int main(void)
{
    int n; cin>>n;
    while(n--)
    {
        int l,r; cin>>l>>r;
        ve.push_back({r,l});
    }
    sort(ve.begin(),ve.end());
    int ans=0;
    int flag=-1e9-5;
    for(int i=0;i<ve.size();i++) if(ve[i].second>flag) ans++,flag=ve[i].first;
    cout<<ans<<endl;
}

906. 区间分组 【有意思】

在这里插入图片描述
https://www.acwing.com/problem/content/description/908/

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct node
{
    int x,y;
}Node[N];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
int main(void)
{
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>Node[i].x>>Node[i].y;
    sort(Node,Node+n,cmp);
    priority_queue<int, vector<int>, greater<int> >heap;
    for(int i=0;i<n;i++)
    {
        if(heap.empty()||heap.top()>=Node[i].x)//组为空,或者任何一个组内都放不下
        {
            heap.push(Node[i].y);
        }
        else if(heap.top()<Node[i].x)//可以放进组内
        {
            heap.pop();//将原来的区间的最右值更新
            heap.push(Node[i].y);
        }
    }
    cout<<heap.size()<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
vector< pair<int,int> > ve;
priority_queue<int, vector<int>, greater<int> >heap;
int l,r,n;
int main(void)
{
    cin>>n;
    while(n--) cin>>l>>r, ve.push_back({l,r});
    sort(ve.begin(),ve.end());
    for(int i=0;i<ve.size();i++)
    {
        if(heap.empty()||heap.top()>=ve[i].first) heap.push(ve[i].second);
        else if(heap.top()<ve[i].first) heap.pop(),heap.push(ve[i].second);
    }
    cout<<heap.size()<<endl;
    return 0;
}

907. 区间覆盖【有意思】

在这里插入图片描述
https://www.acwing.com/problem/content/description/909/
在这里插入图片描述
对于可以覆盖之前的,我们要选它们中最长的。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct node
{
    int x,y;
}Node[N];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
int main(void)
{
    int st,ed; cin>>st>>ed;
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>Node[i].x>>Node[i].y;
    sort(Node,Node+n,cmp);

    int res=0;
    bool flag=false;
    for(int i=0;i<n;i++)
    {
        int j=i,r=-1e9;
        while(j<n&&Node[j].x<=st)//找到最大的右端点
        {
            r=max(r,Node[j].y);
            j++;
        }

        if(r<st)//说明r没有更新,说明这两段没有公共部分,故不能覆盖
        {
            res=-1;
            break;
        }

        res++;
        if(r>=ed)//覆盖完了
        {
            flag=true;
            break;
        }
        st=r;//更新
        i=j-1;
    }
    if(!flag) cout<<-1<<endl;
    else cout<<res<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
vector< pair<int,int> > ve;
int n,l,r,st,ed;
int main(void)
{
    cin>>st>>ed;
    cin>>n;
    while(n--) cin>>l>>r,ve.push_back({l,r});
    sort(ve.begin(),ve.end());
    int ans=0;
    for(int i=0;i<ve.size();i++)
    {
        int j=i;
        bool flag=false;
        int temp=st;
        while(j<ve.size()&&ve[j].first<=temp) flag=true,st=max(st,ve[j++].second);
        if(flag) ans++,i=j-1;
        if(st>=ed) 
        {
            cout<<ans<<endl;
            return 0;
        }
    }
    cout<<-1<<endl;
    return 0;
}

148. 合并果子

在这里插入图片描述
https://www.acwing.com/problem/content/description/150/

方法:每次选最小的两个合并。

小根堆做法

#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
int main(void)
{
    priority_queue<int, vector<int>, greater<int> >heap;
    int n; cin>>n;
    while(n--)
    {
        int x; cin>>x;
        heap.push(x);
    }
    long long int res=0;
    while(heap.size()>1)
    {
        int a=heap.top(); heap.pop();
        int b=heap.top(); heap.pop();
        res+=a+b;
        heap.push(a+b);
    }
    cout<<res<<endl;
    return 0;
}

大根堆做法

#include<cstdio>
#include<queue>
#include<iostream>
using namespace std;
int main(void)
{
    int n; cin>>n;
    priority_queue<int> heap;
    while(n--)
    {
        int x; cin>>x;
        heap.push(-x);
    }
    long long int res=0;
    while(heap.size()>1)
    {
        int a=heap.top(); heap.pop();
        int b=heap.top(); heap.pop();
        res+=(-a-b);
        heap.push(-(-a-b));
    }
    cout<<res<<endl;
    return 0;
}

913. 排队打水

在这里插入图片描述
https://www.acwing.com/problem/content/description/915/

思路: 由于若把时间长的放在后面接水,那么就较少人等,所以排序+贪心即可。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
long long int a[N],s[N];
int main(void)
{
    int n; cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    long long int sum=0;
    for(int i=1;i<=n-1;i++) s[i]=s[i-1]+a[i],sum+=s[i];
    //1代表的就是第二个人的等待时间,第一个人的等待时间为零
    cout<<sum<<endl;
    return 0;
}

104. 货仓选址

在这里插入图片描述

https://www.acwing.com/problem/content/description/106/
选中间的位置最优

详细的证明

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],sum;
int main(void)
{
    int n; cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    int area=a[n/2];
    for(int i=0;i<n;i++) sum+=abs(area-a[i]);
    cout<<sum<<endl;
    return 0;
}

125. 耍杂技的牛

在这里插入图片描述
https://www.acwing.com/problem/content/description/127/

按其和从小到大排序

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct student
{
    int w,s;
}stu[N];
bool cmp(student a,student b)
{
    return a.w+a.s<b.w+b.s;
}
int main(void)
{
    int n;cin>>n;
    for(int i=1;i<=n;i++) cin>>stu[i].w>>stu[i].s;
    sort(stu+1,stu+n+1,cmp);
    int ans=-1e9;
    int res=0;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,res-stu[i].s);
        res+=stu[i].w;
    }
    cout<<ans<<endl;
    return 0;
}
### 贪心算法思想及其应用 贪心算法是一种在每一步选择中都采取当前状态下最优策略的算法设计方法,其目标是希望最终能够达到全局最优解。然而需要注意的是,贪心算法并不总是能保证得到真正的全局最优解[^3]。 #### 贪心算法的核心特点 - **局部最优决策**:每次迭代过程中只考虑当下最有利的选择。 - **不可回溯性**:一旦做出某个决定,则不会更改该决定的结果。 下面是一个经典的活动安排问题作为例子来展示如何运用贪心算法解决实际问题: 假设我们有若干个需要调度的任务列表,每个任务都有自己的起始时间和完成时间。我们的目的是尽可能多地安排这些不重叠的任务。 ```python def activity_selection(s, f): n = len(f) i = 0 result = [] # 将第一个活动加入结果集中 result.append(i) # 遍历剩余的所有活动 for j in range(1, n): if s[j] >= f[i]: result.append(j) i = j return result # 示例数据 s = [1 , 3 , 0 , 5 , 8 , 5] f = [2 , 4 , 6 , 7 , 9 , 9] selected_activities = activity_selection(s, f) print("Selected activities indices:", selected_activities) ``` 上述代码实现了基于结束时间排序后的活动选取过程,确保所选中的每一个新活动都不会与其他已选定的活动发生冲突。 ### 第二章 算法设计与分析 作业解析 对于第二章关于算法设计与分析部分涉及的习题解答,通常会围绕以下几个方面展开讨论: 1. 明确题目要求; 2. 判断是否适合采用某种特定类型的算法(如动态规划、分治或者本节提到的贪心算法); 3. 编写伪代码并逐步优化成正式实现版本; 4. 对复杂度进行理论推导验证效率高低; 具体到某一道具体的练习题上时,可以根据实际情况调整思路方向。比如针对最大连续子序列求和这类经典问题也可以尝试利用类似的思维模式去探索解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值