贪心小记

贪心策略是一种在每次决策是采取当下意义下最优策略的算法。贪心算法,要满足由局部最优解可以推出全局最优解。简单来说,就是把一个很大的问题拆成一个个子问题,然后找出每个子问题的最优解,组合成全局最优解

贪心问题总是只看眼前,并不考虑以后可能造成的影响 。因此在选择贪心算法时,一定要考虑它的正确性。

举个不正确的贪心:noip2011提高组day2第3题观光公交,当年考试时的数据用贪心可以100pts(具体思路可以看题解),然鹅实际上贪心是错的,被别人卡掉了qwq;

贪心的常见做法:

在提高组难度以下的题目中,最常见的贪心有两种。一种是:「我们将 XXX 按照某某顺序排序,然后按某种顺序(例如从小到大)处理」。另一种是:「我们每次都取 XXX 中最大/小的东西,并更新 XXX」,有时「XXX 中最大/小的东西」可以优化,比如用优先队列维护。

排序法:

用排序法常见的情况是输入一个包含几个(一般一到两个)权值的数组,通过排序然后遍历模拟计算的方法求出最优值。

后悔法:

eg:洛谷p2949工作调度

贪心思想:

      1 . 先假设每一项工作都做,将各项工作按截止时间排序后入队。

      2 . 在判断第 i 项工作做与不做时,若其截至时间符合条件,则将其与队中报酬最小的元素比较,若第 i 项工作报酬较高(后悔),则 ans+=a[i].p-q.top()。

       PS :用优先队列(小根堆)来维护队首元素最小。

贪心问题说简单也简单,说难也难,主要在于证明想到的贪心是正确的。

下面举几个贪心算法的经典应用:

1.选择不相交区间问题:

给定n个开区间(ai,bi),选择尽量多个区间,使得这些区间两两没有公共点。

【思路】按照结束时间bi从小到大排序,依次考虑每个事件,如果不会重复,那么就可以选择这个事件。

eg:活动安排

将活动按结束时间排序,排序后从第一个扫到最后一个,如果与前面选到的不冲突就选择这个事件

#include<bits/stdc++.h>

using namespace std;

int n;

struct node{
    int st,ed;
}a[1005];

bool cmp(node x,node y){
    return x.ed<y.ed;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%d%d",&a[i].st,&a[i].ed);
      
    sort(a+1,a+n+1,cmp);
    
    int t=a[1].ed;
    int ans=1;
    
    for(int i=2;i<=n;i++){
        if(a[i].st>=t){
            ans++;
            t=a[i].ed;
        }
    }
    
    cout<<ans<<endl;
}

2.区间选点问题:

给定n个闭区间[ai,bi],在数轴上选尽量少的点,使得每个区间都至少有一个点(不同区间内含的点可以是同一个);

【思路】按结束bi从小到大排序,对于区间,若没有点,则加一个点在队尾(因为队尾是最可能被重复覆盖的地方);

eg:种树

#include<cstdio>
#include<algorithm>
#include<iostream>

using namespace std;

int n,m,k,ans;
bool p[50100];
struct h{
    int b,e,t;
}a[50100];

bool cmp(h x,h y){
    return x.e<y.e;
}

int main(){
    
    cin>>n>>m;
    
    for(int i=1;i<=m;i++)
      cin>>a[i].b>>a[i].e>>a[i].t;
      
    sort(a+1,a+m+1,cmp);
    
    for(int i=1;i<=m;i++){
        k=0;
        for(int j=a[i].b;j<=a[i].e;j++)
          if(p[j]) k++;
        if(k>=a[i].t) continue;
        
        for(int j=a[i].e;j>=a[i].b;j--)
            if(!p[j]) {
                p[j]=1;
                k++;
                ans++;
                if(k==a[i].t) break;
        }
        
    }
    
    cout<<ans;
}

3.区间覆盖问题:

之前讲过了qwq?就是引水入城的线段覆盖中的一种吧qwq

eg:喷水装置

#include<bits/stdc++.h>

using namespace std;

int n,cnt,l,h,x,r;

struct seg{double x,y;}a[20005];

bool cmp(const seg &x,const seg &y){
    return x.x<y.x;
}

void read(){
    cin>>n>>l>>h;
    cnt=0;
    for(int i=1;i<=n;i++){
        cin>>x>>r;
        if(r<=h/2) continue;
        cnt++;
        a[cnt].x=x-sqrt(r*r-h*h/4.0);
        a[cnt].y=x+sqrt(r*r-h*h/4.0);
    }
}

void solve(){
    double t=0;
    int ans=0,bj=1,i=1;
    while(t<l){
        ans++;
        double s=t;
        for(;a[i].x<=s&&i<=cnt;i++)
          if(t<a[i].y) t=a[i].y;
        if(t==s&&s<l) {
            cout<<"0"<<endl;
            bj=0;
            break;
        }
    }
    if(bj) cout<<ans<<endl;
}

int main(){
    int t;
    cin>>t;
    while(t--){
        read();
        sort(a+1,a+cnt+1,cmp);
        solve();
    }
}
原文地址:https://www.cnblogs.com/zhuier-xquan/p/10983451.html