省赛训练题(HDU4968,HDU4970,HDU4864)

来源:互联网 发布:python绝技用的2还是3 编辑:程序博客网 时间:2024/06/06 08:27

省赛训练,关键还是心态问题,心态放平,才能有高效发挥。


这一套题整体感觉质量还是挺好的,各种考验思维的题目,确实很有收获。


按顺序码了前三题,那就让我们来分析一下


A.Improving the GPA


思路:拿到题目后,第一思路就是贪心,压分数的上下值肯定就能得到最值结果。不过,两头的特殊数据就成了比较棘手的问题,考虑分数两边扩展不会影响结果,而扩展到最边上时由于端点的特殊性就可以得到最优值。

于是极限存放60和100,之后贪心其余的步骤。对于贪心的过程,由单步贪心进化到时时贪心。然后对于均分过高或过低的情况单独考虑。

不过对于60和100的数量问题上确实出了不少的问题,最终还是考虑不周而最终报废。

后来经研究发现,既然贪心就完全可以贪心得彻底一点,先将所有数据都处理成60(或100),经以上分析这两个值越多越好。然后对于多余(或不足)的数据,开始依次补进(或消去),直至85(或69),因为进一步的处理不会对分产生任何影响,因此将85(或69)作为为换点标志。

最后就是需要注意,均分过高或过低的情况,由于这种情况下,除最后一个数据外所有值都被设为85(或69),所以最后的结果一定过高(或过低),不过并不影响最终结果,直接按最值100(或60)处理即可。

由此可见,确实贪心过程需要明确的思路和冷静的大脑,不需要将过程复杂化,尽量找出较为普适的同一方程。

最后附上AC代码以及悲催狂WA代码(变量以w开头,位于注释部分),而两个程序结果(***开头为被WA的程序数据)差异经命令行比较(fc)结果如下图。


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>using namespace std;double getscore(int x){    if(x<=69&&x>=60)    {        return 2.0;    }    else if(x<=74&&x>=70)    {        return 2.5;    }    else if(x<=79&&x>=75)    {        return 3.0;    }    else if(x<=84&&x>=80)    {        return 3.5;    }    else if(x<=100&&x>=85)    {        return 4.0;    }    return false;}int main(){    int n,t,score;    while(scanf("%d",&t)!=EOF)    {        /*double wscore[110];        for(int i=60;i<=69;i++)        {            wscore[i] = 2.0;        }        for(int i=70;i<=74;i++)        {            wscore[i] = 2.5;        }        for(int i=75;i<=79;i++)        {            wscore[i] = 3.0;        }        for(int i=80;i<=84;i++)        {            wscore[i] = 3.5;        }        for(int i=85;i<=100;i++)        {            wscore[i] = 4.0;        }*/        while(t--)        {            scanf("%d%d",&score,&n);            double maxscore,minscore;            int totalscore = score * n;            int lastmaxscore = totalscore - 60 * n;            maxscore = 2.0 * (double)(n);            int maxid = n;            while(lastmaxscore)            {                if(lastmaxscore > 25 && maxid > 1)                {                    lastmaxscore -= 25;                    maxscore += 2.0;                }                else                {                    if(lastmaxscore > 40)                    {                        maxscore += 2.0;                    }                    else                    {                        maxscore = maxscore + getscore(lastmaxscore + 60) - 2.0;                    }                    lastmaxscore = 0;                }                maxid--;            }            maxscore /= (double)(n);            int lastminscore = 100 * n - totalscore;            minscore = 4.0 * (double)(n);            int minid = n;            while(lastminscore)            {                if(lastminscore > 31 && minid > 1)                {                    lastminscore -= 31;                    minscore -= 2.0;                }                else                {                    if(lastminscore > 40)                    {                        minscore -= 2.0;                    }                    else                    {                        minscore = minscore + getscore(100 - lastminscore) - 4.0;                    }                    lastminscore = 0;                }                minid --;            }            minscore /= (double)(n);            printf("%.4f %.4f\n",minscore,maxscore);            /*            int wn;            double k;            k = (double)(score);            wn = n;            //max            double wmaxsum = k*(double)(wn);            int wnmax = wn;            int wm60 = (int)(((89.0-k)*(double)(wn))/29.0);            double wmaxs = (double)(wm60)*2.0;            if(k>=85)            {                wmaxs = 4.0;            }            else            {                wmaxsum -= (double)(wm60)*60.0;                wnmax -= wm60;                //int wmaxave = (int)(wmaxsum / (double)(wnmax));                //int wmaxmod = (wmaxave % 5);                //int wmaxan = wmaxave - wmaxmod;                //wmaxs += wscore[wmaxan]*(double)(wnmax-1);                //int maxone = wmaxsum - (double)(wmaxan) * (double)(wnmax - 1);                //wmaxs += wscore[maxone];                //wmaxs /= double(wn);                //wmaxsum -= (double)(wm60)*60.0;                //wnmax -= wm60;                while(wnmax>1)                {                    int wmaxave = (int)(wmaxsum / (double)(wnmax));                    int wmaxmod = wmaxave % 5;                    int wmaxan = wmaxave - wmaxmod;                    wmaxs += wscore[wmaxan];                    wmaxsum -= (double)(wmaxan);                    wnmax--;                }                wmaxs += wscore[(int)(wmaxsum)];                wmaxs /= (double)(wn);            }            //min            double wminsum = k*(double)(wn);            int wnmin = wn;            int wm100 = (int)(((k-65.0)*(double)(wn))/35.0);            double wmins = (double)(wm100)*4.0;            if(k<=69)            {                wmins = 2.0;            }            else            {                wminsum -= (double)(wm100)*100.0;                wnmin -= wm100;                while(wnmin>1)                {                    int wminave = (int)(wminsum / (double)(wnmin));                    int wminmod = 4 - (wminave % 5);                    int wminan = wminave + wminmod;                    wmins += wscore[wminan];                    wminsum -= (double)(wminan);                    wnmin--;                }                wmins += wscore[(int)(wminsum)];                wmins /= (double)(wn);                //int wminave = (int)(wminsum / (double)(wnmin));                //int wminmod = 4 - (wminave % 5);                //int wminan = wminave + wminmod;                //wmins += wscore[wminan]*(double)(wnmin-1);                //int minone = wminsum - (double)(wminan) * (double)(wnmin - 1);                //wmins += wscore[minone];                //wmins /= double(wn);            }            printf("*****%.4f %.4f\n",wmins,wmaxs);           */        }    }    return 0;}





B.Killing Monsters



思路:塔防打怪兽问题,题目很清晰,就是区间线段型数据覆盖处理问题。一开始考虑到用树状数组可以降低求和的时间,然而却发现求和之前,将区间数据分配到对应点上就是个很麻烦的问题。而且,对于测试数据较多的情况,完全可以处理全部点,然后作为静态数据来处理,这就免去了反复运算维护的麻烦,将求和过程变成了独立的操作。

那么,现在最大的问题就是如何处理这些数据数据。

或许,这就有点数学导数的思维了,或者可以说信号里面冲激与阶跃的思想。将区间端点作为突变点来处理,存入第一个attack数组,第二个数组hurt对第一个attack数组进行累加,由突变向总值转变,得到每点的对应伤害值,最后引入第三个blood数组,从后往前累加求和,计算出后项累加和。三个数组的计算是三个相互独立的过程,因此相当巧妙地将时间复杂度降到了线性的O(n),完美解决。

当然,这种思维对于以后处理区间线段数据也会有很好的启示作用。


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>using namespace std;const int maxn = 1e5+10;long long attack[maxn];long long hurt[maxn];long long blood[maxn];int main(){    int n,m,k;    while(scanf("%d",&n)!=EOF)    {        if(n==0)        {            break;        }        memset(attack,0,sizeof(attack));        scanf("%d",&m);        for(int i=0;i<m;i++)        {            int l,r,d;            scanf("%d%d%d",&l,&r,&d);            attack[l] += d;            attack[r+1] -= d;        }        long long temphurt = 0;        for(int i=1;i<=n;i++)        {            temphurt += attack[i];            hurt[i] = temphurt;        }        blood[n] = hurt[n];        for(int i=n-1;i>=1;i--)        {            blood[i] = blood[i+1] + hurt[i];        }        scanf("%d",&k);        int sum = 0;        for(int i=0;i<k;i++)        {            long long h;            int x;            scanf("%lld%d",&h,&x);            if(h>blood[x])            {                sum++;            }        }        printf("%d\n",sum);    }    return 0;}



C.Task



思路:求解最优值,又是一道典型的贪心问题,乍一看有点类似于线性规划。如果只有一维因素,那肯定将最重要的任务分配给最强力的机器。

然而,当问题引入二元时,就不那么简单了,如果仅仅简单的区分两个变量的重要性来排序,当一维变量来排序,就会可能忽略一种很普遍的极端情况(高效高能机器处理中效低能任务,中效中能机器却无法处理低效高能任务)。

所以,开始考虑分别贪心的思路。

首先,当然还是按主次因素将任务和机器从大到小排序。

对于任务和机器两个对象,由于最终要计算与任务相关的金额,所以考虑将任务作为遍历对象。

如果分别遍历的话,O(n^2)的时间复杂的确实难以接受,于是开始考虑优化方法。

于是想到每次将满足第一变量时间效率的机器单独提出处理,这样所有机器都只会被处理一遍,时间复杂度被降为O(n+n)。

而对于被提取出的机器,找出满足要求的第二变量机器功能最小的机器,由于第一变量肯定满足无需再考虑,这样就可以给别的任务留出更高能的机器。然而很尴尬的问题是,就这么个找第二变量最小的满足功能的机器,也可能会成为一个长达O(n)的过程,而最坏情况又一次被降到了无法接受的O(n^2)。

仔细分析,发现,对于被提取出的机器,它的第一变量已经没有任何存在需要了(肯定满足),而两个变量之间的对应关系也就不那么重要了。分析输入数据可以发现,第二变量的取值范围只有100,那么就完全可以将所得机器的第二变量单独处理到一个大小仅为100的level数组中,通过数组计数来实现机器第二变量与任务的匹配,成功将时间复杂的降为O(n*100),完美AC

最后就是数组遍历的过程中细心点,谨防数组越界就好了


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>using namespace std;typedef struct PRO{    int x;    int y;}Pro;bool cmp(const Pro &x1,const Pro &x2){    if(x1.x>x2.x)    {        return true;    }    else if(x1.x==x2.x)    {        if(x1.y>x2.y)        {            return true;        }    }    return false;}const int maxn = 1e5+10;Pro task[maxn];Pro machine[maxn];int level[105];int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        memset(level,0,sizeof(level));        for(int i=0;i<n;i++)        {            scanf("%d%d",&machine[i].x,&machine[i].y);        }        sort(machine,machine+n,cmp);        for(int i=0;i<m;i++)        {            scanf("%d%d",&task[i].x,&task[i].y);        }        sort(task,task+m,cmp);        int sum = 0;        long long money = 0;        for(int i=0,j=0;j<m;j++)        {            while(machine[i].x>=task[j].x&&i<n)            {                level[machine[i].y]++;                i++;            }            for(int k=task[j].y;k<=100;k++)            {                if(level[k]>0)                {                    level[k]--;                    sum++;                    money += 500 * task[j].x + 2 * task[j].y;                    break;                }            }        }        printf("%d %lld\n",sum,money);    }    return 0;}



1 0
原创粉丝点击