• 贪心算法整理


    1.什么是贪心

    贪心算法就是从问题的初始状态出发,经过若干次的贪心选择而得到的最优值(或较优值)的一种求解问题的策略,即贪心策略

    换句话说,贪心策略是在每次选择时采取目前看来是最优的解(也就是局部最优解),通过不断地重复从而得到整个问题的最优解。但是要注意的是:局部最优解并不一定是全局最优解,只有当局部最优解能推出全局最优解的时候,贪心算法才适用

    例题:洛谷P1216数字三角形

    本蒟蒻(刚学OI时)博客:传送门

    这道题如果正着推,虽然你每一步都贪心选择当前情况下的最大值,但是并不能保证这是整个问题的最大值,而如果我们选择使用动态规划,就可以在最后一行选择最大值输出

    但是如果倒着推,从倒数第二行开始,对于每一个点,从它下面的两个选择一个最大的贪心加到这个点上,最后就可以在第一行第一个得到最大值

    2.贪心算法的特点

    1、贪心选择

    所谓贪心选择是指应用同一个规则,江源文体变为一个相似的但规模更小的子问题,而后的每一步都是当前看似最佳的选择,而且这种选择只依赖于已经做出的选择,不依赖于未作出的选择

    2.最优子结构

    执行算法时,每一次得到的结果虽然都是当前问题的最优解,但只有满足全局最优解包含局部最优解是,才能保证最终得到的结果是最优解

    3.几个贪心的实例

    1.选择不相交区间问题(区别于线段覆盖问题)

    给定n个开区间(a,b),选择尽量多的区间,使得这些区间两两没有公共部分

    思路:按照结束点从小到大排序,遍历每一个区间,如果没有和已经选择的活动冲突就选,否则就不选

    例题:活动安排

    传送门

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<time.h>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef pair<int,int> pr;
    const double pi=acos(-1);
    #define rep(i,a,n) for(int i=a;i<=n;i++)
    #define per(i,n,a) for(int i=n;i>=a;i--)
    #define Rep(i,u) for(int i=head[u];i;i=Next[i])
    #define clr(a) memset(a,0,sizeof a)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define sc second
    ld eps=1e-9;
    ll pp=1000000007;
    ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
    ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
    ll read(){
        ll ans=0;
        char last=' ',ch=getchar();
        while(ch<'0' || ch>'9')last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    //head
    
    int n;
    struct activity
    {
        int si,fi;
    }act[1005];
    
    bool cmp(activity a,activity b)
    {
        return a.fi<b.fi;
    }
    
    int main()
    {
        n=read();
        rep(i,1,n)
        {
            act[i].si=read(),act[i].fi=read();
        }
        sort(act+1,act+1+n,cmp);
        int ans=0;
        int t;
        for(int i=1;i<=n;i++)
        {
            if(act[i].si>=t)
            {
                ans++;
                t=act[i].fi;
            }
        }
        cout<<ans;
    } 

    2.区间选点问题

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

    思路:按照右端点从小到大排序,遍历每个区间,如果已经有点包含就转到下一个区间,否则选择最后一个点标记

    因为如果要使需要的点尽可能少,就需要一个点尽可能多的使用,这样只需要在重叠区间标记就可以了

    例题:种树

    传送门

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<time.h>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef pair<int,int> pr;
    const double pi=acos(-1);
    #define rep(i,a,n) for(int i=a;i<=n;i++)
    #define per(i,n,a) for(int i=n;i>=a;i--)
    #define Rep(i,u) for(int i=head[u];i;i=Next[i])
    #define clr(a) memset(a,0,sizeof a)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define sc second
    ld eps=1e-9;
    ll pp=1000000007;
    ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
    ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
    ll read(){
        ll ans=0;
        char last=' ',ch=getchar();
        while(ch<'0' || ch>'9')last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    //head
    
    int n,m;
    bool vis[30005];
    struct people
    {
        int st,ed,nu;
    }peo[30005];
    
    bool cmp(people a,people b)
    {
        return a.ed<b.ed;
    }
    
    int main()
    {
        n=read(),m=read();
        rep(i,1,m) peo[i].st=read(),peo[i].ed=read(),peo[i].nu=read();
        sort(peo+1,peo+1+m,cmp);
        int sum=0;
        rep(i,1,m)
        {
            int t=0;
            rep(j,peo[i].st,peo[i].ed)
            {
                if(vis[j]==1)t++;
                
            }
            if(t>=peo[i].nu)continue;
            per(j,peo[i].ed,peo[i].st)
            {
                if(vis[j]==0)
                {
                    vis[j]=1;
                    t++;
                    sum++;
                    if(t==peo[i].nu)break;
                }
                
            }
        }
        cout<<sum;
    } 

    3.区间覆盖问题


    这是一个比较经典的题目,给定一条线段和n个闭区间[a,b],选择尽可能少的线段,使它们能够覆盖整条线段

    思路:将所有的区间按左端点从小到大排序,依次处理每一个区间,每次选择覆盖点s的区间中右端点最小的一个,直到区间已经包含了这个区间内所有的点为止

    例题:洛谷P1803

    传送门

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<iomanip>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,begin[1000001],end[1000001];
    void inti()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>begin[i]>>end[i];
    }
    void qsort(int x,int y)
    {
        int i,j,mid,t;
        i=x;j=y;mid=end[(x+y)/2];
        while(i<=j)
        {
            while(end[i]<mid) i++;
            while(end[j]>mid) j--;
            if(i<=j)
            {
                t=end[j];end[j]=end[i];end[i]=t;
                t=begin[j];begin[j]=begin[i];begin[i]=t;
                i++;j--;
            }
        }
        if(x<j) qsort(x,j);
        if(i<y) qsort(i,y);
    }
    void solve()
    {
        int ans=0;
        for(int i=1,t=-1;i<=n;i++)
            if(begin[i]>=t) {ans++;t=end[i];}
            cout<<ans<<endl;
    }
    int main()
    {
        inti();
        qsort(1,n);
        solve();
        return 0;
    }

    一般来说比较常用的也就是这些,还有两个部分感觉平常不太能用的到,这里就不写了(以后如果遇到这样的题可能还会写一写)

    总结一下

    1.什么样的题能够用贪心?

      ①可以把大问题分解成很多结构类似的小问题,并且可以对每一个小问题比较轻松的求出当前最优解

      ②局部最优解能推出整体最优解

    2.一些比较常见的贪心的板子

      需要注意的是,一个题如果包含了比较常见的贪心思想(比较难的题),它的贪心做法往往是最难推出来的,但也往往是最优的

      比如:NOIP2010 引水入城 传送门

      

  • 相关阅读:
    LINQ -2015-04-27
    wireshark的安装
    c#中的classes和objects一些知识【1】
    初学C#,用vs去开始hello world!
    file_get_contents HTTP request failed! Internal Server Error
    验证码二(验证码使用)
    接口调用 POST
    接口调用 GET方式
    百度地图改标注样式
    Linux-常用命令
  • 原文地址:https://www.cnblogs.com/lcezych/p/10977828.html
Copyright © 2020-2023  润新知