贪心复习 greedy

没有任何迭代,故若忽略分配数组空间的时间,共需常数时间。

现在越来越感觉消化吸收比做新的题重要。我可能之前花了很多时间把一个题好像搞清楚了。但是人脑是会遗忘的。忘掉之后,需要重新捡起来。所以得多复习以前学过的内容。今天准备复习一下贪心。贪心就是每一步都用最优解,试图得到一个全局最优解。抽象的这种描述可能大家都会。show me the code

#include
#include

using namespace std;

const int N=1e5+10;//数组大小,以后统一用科学计数法,比较简单
int n;
struct Range{//结构体
    int l,r;
    bool operator< (const Range& W)const{
        return r<W.r;//重载小于号,记得要 return ,刚写忘了,怎么也找不到
    }//问题
}range[N];

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        range[i]={l,r};
    }
    sort(range,range+n);//按照右端点排序
    
    int ans=0;
    int ed=-2e9;
    for(int i=0;i<n;i++){
        auto r=range[i];
        if(r.l>ed){//贪心地选择右端点
            ans++;
            ed=r.r;
        }
    }
    
    printf("%d\n",ans);
    
    return 0;
}
#include
#include

using namespace std;

const int N=1e5+10;//非常丝滑,简直完美。但是逻辑是啥呢。为啥这两题的
int n;//代码完全一样。每次选右端点,为什么是正确的。
struct Range{//我们要求的是最大的不相交的区间的数量,要数量最大,
    int l,r;//应该就是要让区间尽可能短吧,好像也不是。和别的区间没有交集
    bool operator< (const Range& W)const{//的区间一定要选上
        return r<W.r;//和别的区间有交集的区间,选一个,对,选尽可能左边
    }//因为右边可能有新的区间,假设我们选靠右边的,比如说样例的那个,我们选
}range[N];//5 这个点,假设有一个区间是 5 到 6 ,那么我们就不能得到最优解3 了
//假设我们选 4 这个点,就可以得到 3 这个最优解(我这里是多加了一个区间来帮助理解
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        range[i]={l,r};
    }
    sort(range,range+n);
    
    int ans=0;
    int ed=-2e9;
    for(int i=0;i<n;i++){
        auto r=range[i];
        if(r.l>ed){
            ans++;
            ed=r.r;
        }
    }
    
    printf("%d\n",ans);
    return 0;
}

第三题就不会写了。看了一下自己之前的笔记。还是有点帮助。哈哈。区间分组

#include
#include
#include//小根堆的头文件

using namespace std;

const int N=1e5+10;
int n;
struct Range{
    int l,r;
    bool operator < (const Range &W)const{
        return l<W.l;//按照左端点排序
    }
}range[N];

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        range[i]={l,r};
    }
    sort(range,range+n);
    
    priority_queue<int,vector<int>,greater<int>> heap;//小根堆
    heap.push(range[0].r);//把最左边的区间的右端点存进去
    for(int i=1;i<n;i++){//枚举剩下的区间
        if(range[i].l>heap.top()){//可以放到同一组
            heap.pop();//更新这一组的右端点
            heap.push(range[i].r);
        }else{//不能放到同一组
            heap.push(range[i].r);
        }
    }
    
    printf("%d\n",heap.size());
    return 0;
}

感觉也不难了。多亏有之前的笔记。自己看自己的笔记,比看别人的题解更加清晰。因为属于个性化的内容。感觉个性化这个东西非常非常重要。下一个题也不会,但是自己做的笔记不是那么详细,记忆像是消磁了一样。果然好记性不如烂笔头。区间覆盖这个题,结合了双指针。前面两个题是右端点排序,后面两个题是左端点排序。最关键的一个点就是,我们需要更新我们需要覆盖的区间。覆盖掉一段,就把区间的左端点用使用的区间的右端点覆盖掉。我们要找的是最靠近需要覆盖区间的区间。

#include
#include

using namespace std;

const int N=1e5+10;
int n;
int st,ed;
struct Range{
    int l,r;
    bool operator < (const Range& W)const{
        return l<W.l;//左端点排序
    }
}range[N];

int main(){
    scanf("%d%d",&st,&ed);
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        range[i]={l,r};
    }
    sort(range,range+n);
    
    int ans=0;
    bool success=false;
    for(int i=0;i<n;i++){
        int j=i;//双指针
        int r=-2e9;
        while(j<n&&range[j].l<=st){//找最靠近目标区间左端点的区间
            r=max(r,range[j].r);
            j++;
        }
        
        if(r<st){//找完了都靠近不了左端点,说明失败了
            break;
        }
        
        ans++;//靠近了左端点,说明 j 这个区间的左端点在目标区间的左端点的
        //右边了,这个时候我们下一次枚举要从前面那个区间开始枚举,也就是说
        //i=j-1 ,是不是还有点迷糊,举个例子,比如说,3 这个区间在目标区间的右边了
        //2 这个区间在目标区间的左边。然后我们会把目标区间的左端点更新为 3 这个区间在目标区间的左边。然后我们会把目标区间的左端点更新
        //区间的右端点。那我们下一次循环还是得从 2 这个区间开始循环。相当于我们已经选择了
        //3 这个区间。好像不是这个问题,是因为 for 循环 i++ 吧。多加一次会跳过一个
        //区间
        if(r>=ed){
            success=true;
            break;
        }
        
        st=r;
        i=j-1;//有点烧脑,为什么是 j-1 而不是 j 呢。哦哦。因为前面有一个 i++ 
    }
    
    if(!success){
        ans=-1;
    }
    printf("%d\n",ans);
    
    return 0;
}

小根堆

#include
#include
#include

using namespace std;

int n;
priority_queue<int,vector<int>,greater<int>> heap;//小根堆

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int x;
        scanf("%d",&x);
        heap.push(x);//把所有的元素存进小根堆
    }
    
    int ans=0;
    while(heap.size()>1){
        int a=heap.top();
        heap.pop();
        int b=heap.top();
        heap.pop();//把最小的两个元素取出来
        ans+=a+b;//累加
        heap.push(a+b);//更新为新的一堆果子
    }
    
    printf("%d\n",ans);
    return 0;
}

排队打水

#include
#include

using namespace std;

const int N=1e5+10;
int n;
int a[N];

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    sort(a,a+n);
    
    long long ans=0;//好像会爆 int 
    for(int i=0;i<n;i++){
        ans+=(n-i-1)*a[i];//前面的等待的时间后面都是要计算进去的
    }
    printf("%lld\n",ans);
    
    return 0;
}

货仓选址

#include
#include

using namespace std;

const int N=1e5+10;
int n;
int a[N];

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    sort(a,a+n);//排序
    
    long long distance=0;
    for(int i=0;i<n;i++){
        distance+=abs(a[n/2]-a[i]);//选中点作为位置
    }
    printf("%lld\n",distance);
    
    return 0;
}

#include
#include

using namespace std;

const int N=5e4+10;
int n;
struct Cow{//结构体按照 重量+强壮程度 排序
    int total,weight,strength;
    bool  operator< (const Cow& W)const{
        return total<W.total;
    }
}cow[N];

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int weight,strength;
        scanf("%d%d",&weight,&strength);
        int total;
        total=weight+strength;
        cow[i]={total,weight,strength};
    }
    sort(cow,cow+n);//排序
    
    int ans=-0x3f3f3f3f;//初始化为负无穷
    int total_weight=0;//当前牛的上面的所有牛的重量
    for(int i=0;i<n;i++){
        ans=max(ans,total_weight-cow[i].strength);
        total_weight+=cow[i].weight;//更新上面所有牛的总重量
    }
    printf("%d\n",ans);
    
    return 0;
}

总共是八个题,前面四个相对难一些,后面四个题比较直观。然后新的一些数据结构就是结构体和小根堆。贪心的证明其实不用太管。能理解这个贪心的思想就好了。

你可能感兴趣的:(lanqiao,cup,算法)