nyoj贪心算法

在nyoj上做了不少贪心算法的题了,作为一个很重要的基础算法,贪心还是有很强的规律性的。在这篇博文里简单总结一下,欢迎大家多提宝贵意见。


第一、二、三类为几种经典的贪心算法,都涉及到若干区间,而我们第一步要做的都是将这些区间按右端点排序(通常为从小到大排序,若右端点一致则按左端点从小到大排序)。


第一类,用最少点数覆盖给定区间左右端点的所有区间,要求每个区间内至少一个点,如nyoj287、nyoj891和nyoj1036。方法是将所有区间按右端点从小到大排序,记录第一个区间右端点值tmp,并依次判断每个区间的左端点是否在tmp左侧,若不是,更新tmp为当前区间右端点并将计数值++;


第二类,能安排活动最大数,如nyoj14。14题首先将各活动按右区间端点排序,记录第一个区间右端点值tmp,依次找区间左端点是否在tmp右边。若是,更新tmp为当前区间右端点并将计数值++。


第三类,给定一些区间,若某区间能像套娃一样互相包含(不允许一直接套二,但可以一套一套一),则可将其视为一个区间,求这些区间最少可视为几个区间,如nyoj236。此题要设置一个标记数组,先按右端点大到小排序,记录第一个区间左端点值tmp,更新第一位标记数组,依次判断之后的区间,若左区间大于tmp,证明该区间可被囊括,将tmp更新为该区间左端点值,并更新标记数组对应位置。用此方法判断至最后一个区间为以第一个区间为最外侧“套娃”最多能包含哪些区间。再进行第二轮,此时最外侧的区间为从未使用过的第一个区间。重复以上步骤,看有多少个大“套娃”即为所求。


第四类,多项事件并发求最短时间。如nyoj220,方法是依次将每项事件占用的资源(如时间,房间等)++,最终所有资源中最大数值即为所求;


第五类,给定一个数字,规定某种变换方式,求若干步变换后的最大值或最小值,如nyoj448和nyoj1057等。一般考虑每一步都是最优的变换方法,不过像1057这道题就不能逐步考虑,由此题题干可知,若可以移动两次,那么第1、2、3位均可以移动到第一位,这样先挑选前三位中的最大的数字(一样的话取靠前位),在总步数上扣去选定位和当前位(第0位)位数差后看还剩多少步。若还剩余移动步数,则用同样的方法挑选第二位,直到挑选完倒数第二位或没有步数时停止。附上本人此题ac代码:

#include
using namespace std;

int finds(string s,int be,int a)
{
    int max=s[be];
    int ans=be;
    int ss=s.size();
    for(int i=be;i<=be+a&&imax)
        {
            max=s[i];
            ans=i;
        }
    }
    return ans;
}

int main()
{
    string s;
    int a;
    while(cin>>s>>a)
    {
        int ss=s.size();
        int be=0;
        while(a&&(bebe;k--)
            {
                s[k]=s[k-1];
            }
            s[be]=tmp;
            a-=(i-be);
            be++;

        }
        cout<


第六类,二分加贪心,如nyoj586和nyoj619。以其中的586题(疯牛)为例,需要找到能安排下所有牛的最大间隔最小值。问题读起来有点绕,可以理解为让这些牛尽量在一条直线上给定的房间分散居住,后求出最分散的情况下,两头牛的距离最小是多少。用贪心的方法解决需要先将房间位置排序,后判断对于给定的间隔,最多能放几头牛。相关代码如下:

int fun(int m)
{
    int cnt=1;
    int tmp=room[0];
    for(int i=1;i=m)
        {
            cnt++;
            tmp=room[i];
        }
    }
    //cout<<"can live"<

数据量给的10的9次幂,所以要用到二分,两个边界值我分别选取的相邻栅栏的位置差(题中规定相邻不是位置差一)和首位栅栏位置差/(牛个数-1)(n头牛有n-1个间距)。顺便说下,我在二分的时候习惯在边界出现左大于右时跳出循环,由于循环体内达到这种条件,上次一循环必定是左右边界重合,故只需检验边界值是否成立即可,这道题二分相关的代码如下

        sort(room,room+n);
        int l=1000000002;
        int k=room[n-1]/(c-1);
        for(int i=1;i

k、l重合时,若重合点不能满足住下c头牛,则k-1后跳出循环,此时l指向的值依然是重合点,不满足条件,故输出l-1;类比重合点满足,则l+1后跳出循环,重合点满足而其右边在之前判断中一定不满足(因为k移动到重合点了),同样输出l-1;619题于此题类似,只是在设计贪心部分时要考虑相等和小于两种情况。


第七类,找规律问题,如nyoj1087。将从1至n*n这些数放在一个n*n的矩阵中,其中数值相差1的数必须在矩阵中相邻,求对角线数字和最大值。既然希望达到和的最大值,那我们不妨将n*n放在第一行第一个位置,之后从大到小的填写其他数字,并尽量将大的数放在对角线上。比如6*6的矩阵就可以首先这样摆放:

36   35


       34


       33  32   31


                    30


                    29  28  


其中变红位置为对角线位置,但之后不能将27,26继续按以上规律填写,否则27会将矩阵分割成右上和左下两个部分,导致无法继续填写数字。故27要填写在28上方,并将数字充满上方之后才能填写28右侧的数字,效果如下:

36   35   22    21  20  19


  1   34  23   24  25  18


  2   33   32   31  26  17


  3    4     5    30  27  16


  8    7     6    29  28  15


  9   10   11   12   13  14 

由于对称性,右下角的数字刚好为其左上方数字的二分之一,得出规律:对角线和的最大值为首项n*n,公差-2,项数n-1的等差数列之和加上其最后一项的二分之一。


第八类:过河问题,如nyoj47。也属于经典问题,解决方法比较固定,先将人们的过桥时间按从小到大排序。

①若只有1人,直接通过,时间为a[0];

②若只有2人,一起通过,时间为a[1];

③若只有3人,最快和最慢先通过,最快人回来带着第二快过河,时间为a[0]+a[1]+1[2];

④若大于3人,若2*a[1]+a[0]+a[n-1]>2*a[0]+a[n-1]+a[n-2],则最快人连送两次,否则,最短2人过去后,最短回,最长两人过,次短回;这两种方法均能送最长二人过河,之后转化为n-2人过河。


第九类:贪心算法中的背包问题,如nyoj106、nyoj824、nyoj284。解决此类问题的方法是先将每种物品的价格(或开销)按照从小到大(或从大到小)排序,依次尽量多的购买,直至打到目标要求(如重量要求等)。

你可能感兴趣的:(nyoj贪心算法)