贪心算法整理

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 引水入城 传送门

  

 

转载于:https://www.cnblogs.com/lcezych/p/10977828.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值