AcWing算法基础课贪心模板题

本文探讨了区间问题的两个子任务:区间选点和最大不相交区间数量。通过排序策略(右端点递增和左端点递增),给出最优解的证明,并提供了C++代码实例。同时涉及排序不等式(如排队打水)和中位数定理(如货仓选址)的应用。
摘要由CSDN通过智能技术生成

一、区间问题

区间选点最大不相交区间数量

题解:

1)如何选点?

(1)将每个区间按右端点递增排序、

(2)从前往后遍历每个区间,如果当前区间中已经包含点,跳过,否则选取当前区间的右端点

PS:此方法选择的点必然是序列中无交集的区间的数量,所以此方法适用于两题

2)证明?

最优解 a n s ans ans:所有合法方案中的最小值,贪心解 c n t cnt cnt:上述选法

(1)上述选法必然会使每个区间里至少包含一个点,是一种合法方案,所以 a n s < = c n t ans<=cnt ans<=cnt

(2)通过贪心方案我们知道序列中包含了 c n t cnt cnt个相互之间没有交集的区间,所以对于一个合法方案,必然至少包含 c n t cnt cnt个点,所以 c n t < = a n s cnt<=ans cnt<=ans

#include<bits/stdc++.h>
#define r first
#define l second
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
PII g[N];
int n;
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={b,a};
    }
    sort(g,g+n);
    int ans=1,pos=g[0].r;
    for(int i=0;i<n;i++){
        if(g[i].l>pos){
            pos=g[i].r;
            ans++;
        }
    }
    cout<<ans;
}

按照左端点递增排序,选左端点做法:

#include<bits/stdc++.h>
#define l first
#define r second
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
PII g[N];
int n;
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={a,b};
    }
    sort(g,g+n);
    int ans=1,pos=g[n-1].l;
    for(int i=n-1;i>=0;i--){
        if(g[i].r<pos){
            pos=g[i].l;
            ans++;
        }
    }
    cout<<ans;
}

区间分组

题解:

1)方案?

(1)将所有区间按左端点递增排序

(2)从左往右枚举每个区间,判断能否放进现有的组中,能则更新选择组的 m a x r maxr maxr,否则新开一个组

2)证明?

最优解 a n s ans ans,贪心解 c n t cnt cnt

(1)对于贪心解 c n t cnt cnt来说,一定可以使每个区间被包含到某个组中,所以 a n s < = c n t ans<=cnt ans<=cnt

(2)对于贪心解 c n t cnt cnt,每一组所包含的区间一定有交集,否则不会新开一个组,所以对于一个合法方案至少包含 c n t cnt cnt个组,所以 c n t < = a n s cnt<=ans cnt<=ans

左端点递增排序

#include<bits/stdc++.h>
#define l first
#define r second
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
int n;
PII g[N];
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={a,b};
    }
    sort(g,g+n);
    priority_queue<int,vector<int>,greater<int>>q;
    q.push(g[0].r);
    for(int i=1;i<n;i++){
        auto [l,r]=g[i];
        int t=q.top();
        if(t<l){
            q.pop(),q.push(r);
        }
        else q.push(r);
    }
    cout<<q.size();
}

右端点递增排序

#include<bits/stdc++.h>
#define r first
#define l second
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
int n;
PII g[N];
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={b,a};
    }
    sort(g,g+n);
    priority_queue<int>q;
    q.push(g[n-1].l);
    for(int i=n-2;i>=0;i--){
        auto [r,l]=g[i];
        int t=q.top();
        if(t>r){
            q.pop(),q.push(l);
        }
        else q.push(l);
    }
    cout<<q.size();
}

巨巨的做法:(扫描线)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
int g[N*2];
int main(){
    cin>>n;
    int cnt=0;
    for(int i=0;i<n;i++){
        int l,r; cin>>l>>r;
        g[cnt++]=l*2,g[cnt++]=r*2+1;
    }
    sort(g,g+cnt);
    int ans=0,now=0;
    for(int i=0;i<cnt;i++){
        if(g[i]%2) now--;
        else now++;
        ans=max(ans,now);
    }
    cout<<ans;
}

区间覆盖

题解:

1)方案?

(1)将所有区间按左端点递增排序

(2)从左往右遍历每个区间,在所有能覆盖 s t st st的区间中,选择右端点最大的区间,然后将st更新右端点的最大值

2)证明?(反证法

最优解 a n s ans ans,贪心解 c n t cnt cnt

不考虑无解情况

假设 a n s ans ans c n t cnt cnt在第 i i i个区间不同,由于贪心解找的是能覆盖st的情况下右端点最大的区间,所以当前 a n s r < c n t r ans_r<cnt_r ansr<cntr,对于最优解的第 i + 1 i+1 i+1区间 x x x a n s r > = x l ans_r>=x_l ansr>=xl,所以必然有 c n t r > x l cnt_r>x_l cntr>xl,所以最优解在第 i i i个位置即使替换为与贪心解相同的区间也不会影响选取的区间个数,即使之后仍有与贪心解中不同的区间,也可将最优解中的区间替换为对应的贪心解中的区间,所以 a n s = c n t ans=cnt ans=cnt

#include<bits/stdc++.h>
#define l first
#define r second
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
int n,st,ed;
PII g[N];
int main(){
    cin>>st>>ed>>n;
    for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={a,b};
    }
    sort(g,g+n);
    int ans=0;
    bool f=0;
    for(int i=0;i<n;i++){
        int j=i,pos=-2e9;
        while(j<n&&g[j].l<=st) pos=max(g[j].r,pos),j++;//找最靠右的端点
        if(pos<st){
            ans=-1;
            break;
        }
        ans++;
        if(pos>=ed){
            f=1;
            break;
        }
        st=pos;
        i=j-1;
    }
    if(!f) ans=-1;
    cout<<ans;
}

二、排序不等式

排队打水

题解:

如何算每个人的等待时间?

对于第 i i i个人而言,它后面的人必然要等它打完水,所以对于第 i i i个人,所有人花费在它身上的等待时间为 s [ i ] ∗ ( n − i ) s[i]*(n-i) s[i](ni),所以应该按照每个人打水时间递增的顺序打水才能让总的打水时间最短

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n;
int s[N];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>s[i];
    sort(s+1,s+n+1);
    ll ans=0;
    for(int i=1;i<n;i++) ans+=s[i]*(n-i);
    cout<<ans;
    return 0;
}

三、中位数定理

货仓选址

题解:

找中位数

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    int sum=0;
    for(int i=0;i<n;i++) sum+=abs(a[i]-a[n/2]);//当n为偶数时取左中右中均可
    cout<<sum;
}

四、推公式

耍杂技的牛

题解:

如何构造最小的最大风险值?

局部调整法

i i i头牛,第 i + 1 i+1 i+1头牛的风险值为:
第 i 头 牛 : a n s i = w 1 + w 2 + . . . + w i − 1 − s i 第 i + 1 头 牛 : a n s i + 1 = w 1 + w 2 + . . . + w i − 1 + w i − s i + 1 第i头牛:ans_i=w_1+w_2+...+w_{i-1}-s_i\\ 第i+1头牛:ans_{i+1}=w_1+w_2+...+w_{i-1}+w_i-s_{i+1} iansi=w1+w2+...+wi1sii+1ansi+1=w1+w2+...+wi1+wisi+1
交换这两头牛在序列中的顺序:
第 i 头 牛 : a n s i = w 1 + w 2 + . . . + w i − 1 + w i + 1 − s i 第 i + 1 头 牛 : a n s i + 1 = w 1 + w 2 + . . . + w i − 1 − s i + 1 第i头牛:ans_i=w_1+w_2+...+w_{i-1}+w_{i+1}-s_i\\ 第i+1头牛:ans_{i+1}=w_1+w_2+...+w_{i-1}-s_{i+1} iansi=w1+w2+...+wi1+wi+1sii+1ansi+1=w1+w2+...+wi1si+1
C = ∑ j = 1 i − 1 w j C=\sum_{j=1}^{i-1}{w_j} C=j=1i1wj

得:

变化第i个位置上的牛第i+1个位置上的牛
交换前 C − s i C-s_i Csi C + w i − s i + 1 C+w_i-s_{i+1} C+wisi+1
交换后 C + w i + 1 − s i C+w_{i+1}-s_i C+wi+1si C − s i + 1 C-s_{i+1} Csi+1

化简得

变化第i个位置上的牛第i+1个位置上的牛
交换前 − s i -s_i si w i − s i + 1 w_i-s_{i+1} wisi+1
交换后 w i + 1 − s i w_{i+1}-s_i wi+1si − s i + 1 -s_{i+1} si+1

什么情况下交换前的 m a x > max> max> 交换后的 m a x max max?

1)显然 w i − s i + 1 > − s i + 1 w_i-s_{i+1}>-s_{i+1} wisi+1>si+1

2) w i + s i > w i + 1 + s i + 1 w_i+s_i>w_{i+1}+s_{i+1} wi+si>wi+1+si+1时,有 w i − s i + 1 > w i + 1 − s i w_i-s_{i+1}>w_{i+1}-s_i wisi+1>wi+1si

所以当 w i + s i > w i + 1 + s i + 1 w_i+s_i>w_{i+1}+s_{i+1} wi+si>wi+1+si+1时,一定有交换前的风险最大值= m a x ( − s i , w i − s i + 1 ) > max(-s_i,w_{i}-s_{i+1})> max(si,wisi+1)> 交换后的风险最大值

所以当按照 w + s w+s w+s递增顺序排列时,会取得最小的风险最大值

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
PII g[N];
int n;
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[i]={a+b,b};
    }
    sort(g,g+n);
    int ans=-2e9-10,res=0;
    for(int i=0;i<n;i++){
        auto [w,s]=g[i];
        ans=max(ans,res-s);
        res+=w-s;
    }
    cout<<ans;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: acwing算法基础课是一门针对算法学习的在线课程,在这门课程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础课网盘是一个用于存储课程资源的平台。通过这个网盘,学生可以下载课程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照课程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础课网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解课程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础课网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础课是一门优质的算法学习资源,其中的课程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门课程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础课也提供了网盘服务,方便学习者下载课程资料并进行学习。 通过acwing算法基础课网盘,学习者可以方便地获取到课程的各种学习资料,包括讲义、习集、代码示例等。这些资料可以帮助学习者更好地理解和掌握课程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础课网盘的优点不仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少数据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者不同的需求。 总的来说,acwing算法基础课网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的课程!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值