浅谈贪心

来源:互联网 发布:出国旅游的好处知乎 编辑:程序博客网 时间:2024/06/16 05:37

前言

贪心是OI里常见的一种题,与DP长得很像,但个性完全不一样;有时候容易掉入贪心的坑里想不到DP,有时却只认为DP是对的而想不到贪心,所以,特此来做一个总结,浅谈一下我所遇到的贪心题,巩固基础.

T1:

https://jzoj.net/senior/#contest/show/1951/1

Problem:

要求在一个[1..n]的区间里种树,有h个特殊区间,每个特殊区间的范围[li..ri]中至少要有ti颗树,求最少的树的数量.

Constraint:

30%的数据满足0<n<=10000<h<=500
100%的数据满足n<=30000h<=5000

Solution:

考虑贪心.

我们按照ri从小到大排序.

那么贪心的思路就是,每次植树时尽量在区间的尾部植树,这样才可能会与后面结束的区间重复,尽量种少树.

不难理解,因为数据太弱,直接暴力求解即可.

Code:

var        sum:array[0..300000] of longint;        st,en,t:array[1..5000] of longint;        n,h,i,j,x,ans:longint;procedure sort(l,r:longint);var        i,j,mid,p:longint;begin        i:=l; j:=r;        mid:=en[(l+r) shr 1];        while i<j do        begin                while (en[i]<mid) do inc(i);                while (en[j]>mid) do dec(j);                if i<=j then                begin                        p:=st[i]; st[i]:=st[j]; st[j]:=p;                        p:=en[i]; en[i]:=en[j]; en[j]:=p;                        p:=t[i]; t[i]:=t[j]; t[j]:=p;                        inc(i); dec(j);                end;        end;        if l<j then sort(l,j);        if i<r then sort(i,r);end;begin        readln(n,h);        for i:=1 to h do                readln(st[i],en[i],t[i]);        sort(1,h);        for i:=1 to h do        begin                x:=0;                for j:=st[i] to en[i] do                        inc(x,sum[j]);                if x>=t[i] then continue;                x:=t[i]-x;                inc(ans,x);                j:=en[i];                while x>0 do                begin                        if sum[j]=0 then                        begin                                sum[j]:=1;                                dec(x);                        end;                        dec(j);                end;        end;        writeln(ans);end.

T2

https://jzoj.net/junior/#main/show/1138

T3

https://jzoj.net/senior/#contest/show/1922/2

T4

https://jzoj.net/senior/#contest/show/1937/1

Problem

n种石桥,每种石桥对应两个参数p[i],v[i],还有m种原料,也对应两个参数h[i],d[i],我们要保证每一个石桥i对于一种修理原料jh[j]>=p[i],d[j]>=v[i],这样才可用原料j修理石桥i,且所需要的费用为h[j],求使得所有石桥被修复的最少费用.

Data constraint

这里写图片描述

Solution

分析一下题意,题意是让我们从m个二元组中找出n组与另外n个二元组配对,并使得n个二元组当中的每一个二元组满足h[j]>=p[i],d[j]>=v[i],且使得hi最小.

注意这题的关键是每一个二元组都需要被配对.

简单的思路,既然要hi最小,那我就按hi去贪心.

我把pi为第一关键字,vi为第二关键字,按从小到大排个序.

每次对于一个二元组(pi,vi),找出对应的最优的hj,dj,并满足题目所述条件.

每次找最优的应按照hj尽量小的情况下dj尽量小.

但显然这样做是不对的,因为这样子的贪心不会满足题目的关键:每一二元组都要配对.

仔细想想可以发现,如果按照hj尽量小贪心,那如果下次来一个二元组(pi+1,vi+1),而vi+1非常大,只能用(hj,dj)去配对,那么这样就找不到一个新的二元组(hj,dj)去与之配对了.

所以我们需要换一个思路贪心.

我们发现,以vi为第二关键字的贪心都是错的(本题贪心关键

错的原因在于我们贪心的是h的值,而我们又把h对应的p做为第一关键字,p做为第一关键字,自然h也跟着是第一关键字了,那么就无暇考虑到v了,所以,我们应当改正贪心思路:

我们按照v从大到小排个序,这样子能保证每次使得p的最优情况下v也最优,那么两个条件都最优了,最终答案一定最优.

注意每次选数时一定要选最合适的,即在保证hj尽量小的情况下dj也尽量小.

这样子本题就可以拿到60分了,而100分的算法则用了平衡树来维护,即每次找到一个最合适的数这部分可以优化.

T5

https://jzoj.net/senior/#contest/show/1983/0

Problem

给定一串数,把其中一个数ai变成h,需要|aih|的费用,使变化后整个序列相邻数的差的绝对值之和最小.

Data constraint

对于10%的数据,有N≤10;
对于30%的数据,有A[I]≤1000;
对于40%的数据,有N≤1000;
对于100%的数据,有N≤100000,A[I]≤10000000。

Solution

如果对于一个数ai,满足ai1<ai<ai+1,那么ai改动,不会使答案更优.

因为我们要明白,对于一个数ai,它只对相邻的两个数有价值

也就是无后效性,我们不管它怎么变,与ai+1变没有影响.

怎么更深入的理解“与ai+1没有影响呢”呢?

我们可以举几个例子去验证,当然也可以感性加理性的理解.

不难发现,如果对于一个数ai,改变它时,造就了ai+1的影响,那么这个影响不会使得答案更优.

因为ai+1被影响时,也需要付出相应的代价,而当前正在修改的是ai的值,ai+1只对aiai+2有价值,所以答案不会更优.

当然这么理解也有点感性了,其实可以讨论一下ai1,ai,ai+1的关系,依次列举几个数,就会发现答案的正确性.

那么贪心的策略就很显然了:

对于形如ai1>ai<ai+1,我们就把ai变为相应的较小值和较大值,这样做,答案肯定是最优的.

pay attention to:

像这一类n,ai都很大,又不知如何下手的题目,可以考虑贪心,当自认为找到一种合理的贪心,但又被自我否定时,要多举几个例子去验证,不要一贯的跟着自己的思路走.

有时严谨并不严谨.

T6

https://jzoj.net/senior/#main/show/1220

T7

https://jzoj.net/senior/#main/show/3804

T8

https://jzoj.net/senior/#main/show/3822

T9

https://jzoj.net/senior/#contest/show/2045/1

Problem

给定n个任务,每个任务会有一个时刻和价值,每一时刻只能做一个任务,且超过时刻的任务不可做,求最大价值.

Data constraint

20%的数据,n<=100。
40%的数据,n<=50000。
100%的数据,n<=200000,快乐值的绝对值不超过32767,时刻非负且小于2^31。

Solution

做过一道原题,可再做一遍还是没做出,可悲!

一开始就想到了维护一个小根堆,可惜贪心的思想错了;

我考场时按价值排了序,但很明显这样是错的,后来我想到用线段树去维护,结果价值为负数的没判断,时刻为0的没判断,就这么少了60分.

用线段树可以拿90.

但贪心可以拿100.

我们看看如何正确地贪心:

我们可以按照时间从小到大排序,顺序枚举第i个任务,且保证每一个任务的时限必须大于k(k一开始等于0),如果满足,我们就把第i个任务放进堆里,并把k+1,随后如果一个任务不满足,我们就与堆顶判断看看是否能替换使得答案更优.

这不需要证也可以感性地理解一定是最优的.

T10

https://jzoj.net/senior/#contest/show/2045/2

Problem

给出N个点,每个点有且只有一个被其限制的点,要求选出一个最大的点集,使得每一个集合内的点都有一个集合外的点限制它。

Data constraint

30%的数据,n<=10。
60%的数据,n<=10^5。
100%的数据,a_i<=n<=10^6。

Solution

这题考场时没什么想法,但其实用贪心的思想做并不难.

【模型转换】

我们把“限制”的关系转化成一条边,即若y会被x限制,则连一条从x到y的有向边。那么原图就会变成了若干个联通块,每个联通块必定由一个环以及若干棵“挂”在环上的树组成,即环套树。

【贪心】

现在考虑贪心。

找出所有没有入度的点,那么这些点必定不能选入集合中,我们沿着这些点上溯,将可以选进集合的点都选进集合。

选入的集合可以再继续上溯,我们发现这些点会被选中,那么其联向的点的入度必然会减1,如果为0,则又可以继续上述操作,以此类推,直到没有点可被选入“点集”中.

最后再把剩下点做一遍环处理,注意标记就好了.

经典模型

硬币问题

有1 元、5 元、10 元、50 元、100 元、500 元硬币若干枚,现在要用这些
硬币来支付A 元,求最少需要多少枚硬币?假定至少存在一种方案

解法

每个大面值硬币均为小面值硬币倍数
应尽可能多的使用大面值硬币

区间问题

给定n 个区间,每个区间左右端点分别为li,ri,现在要求选出尽量多的区
间使得它们两两不相交(不包括端点),问最多能选出几个区间

解法

考虑把ri从小到大排序;
证明:如果第i个区间不选,选第i+1个区间,则相当于[l1,l2]这段无用.
且影响i+2更多,不可能使答案更优;

选点问题

给定n 个区间[li; ri],取尽量少的点使得所有区间内部至少一个点

解法

将所有区间按ri 从小到大排序,ri 相同按li从大到小排序
按顺序考虑每个区间,若当前区间内没点,则在ri 处放一个点.

顺序问题

给定n 个数ai,再给出n 个数bi,现在要求你重新排列b 的顺序,使得

i=1naibi
最小.

解法

由排序不等式可直接得出结果,即逆序和最小

字典序最小问题

给定长度为n 的字符串S,现要构造一个长度为n 的字符串T. 初始T 为
空串,随后可以从S 头部删除一个字符加到T 的尾部,或是从S 尾部删
除一个字符加到T 的尾部,求字典序最小的T

解法

由字典序比较方式知,T 越靠前的位置越小越好
操作是从前到后一位位构造T,因此每次贪心取S 头尾两个字符中字典序
较小的那个即可
若头尾字典序一样就继续比较下一位
通常字典序最小问题均是一位位贪心考虑

0 0
原创粉丝点击