zoukankan      html  css  js  c++  java
  • 贪心法(二):贪心法的应用例题

            下面我们通过解决洛谷题库中的几道应用贪心法思想编写程序的例题,进一步体会贪心法的应用。

    【例1】纪念品分组。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/P1094)。

    题目描述

    元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。

    你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

    输入格式

    共3行:

    第一行包括一个整数 w,为每组纪念品价格之和的上上限。

    第二行为一个整数 n,表示购来的纪念品的总件数。

    第三行为n个正整数,每个正整数 Pi表示所对应纪念品的价格。

    输出格式

    一个整数,即最少的分组数目。

    输入样例

    100

    9

    90 20 20 30 50 60 70 80 90

    输出样例

    6

           (1)编程思路。

            将纪念品按价格从小到大排好序。由于分组时每组最多只能有两个纪念品,因此基于贪心法分组时,总是将当前价值最大和价值最小的纪念品进行组合,若总价值没有超过给定的整数,则将它们分为一组;若总价值超过了给定的整数,则将价格大的纪念品单独作为一组,如此循环直至所有纪念品分组完毕,即可求出最少的分组数目。

           (2)源程序。

    #include <stdio.h>

    #include <algorithm>

    using namespace std;

    int main()

    {

       int g;

       scanf("%d",&g);

       int n;

       scanf("%d",&n);

       int p[30001];

       int i,j,tmp;

       for (i=0;i<n;i++)

       {

          scanf("%d",&p[i]);

       }

       sort(p,p+n);      // 纪念品价格从小到大排列

       int ans=0;

       i=0;  j=n-1;

       while (i<j)

       {

           if (p[i]+p[j]>g) j--;

           else

           {

               i++; j--;

           }

           ans++;

       }

       if (i==j) ans++;

       printf("%d\n",ans);

       return 0;

    }

     【例2】排队接水。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/P1223)。

    题目描述

    有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti ,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。

    输入格式

    第一行为一个整数 n。

    第二行n 个整数,第i 个整数 Ti,表示第i个人的接水时间Ti 。

    输出格式

    输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

    输入样例

    10

    56 12 1 99 1000 234 33 55 99 812

    输出样例

    3 2 7 8 1 4 9 6 10 5

    291.90

          (1)编程思路。

            设有n个人打水,每个人的接水时间分别为T1,T2,...,Tn

    ​        由于对于每一个人,在场剩余每个人都要等待一次他的接水,则接水等待时间总和为T1*(n-1)+T2*(n-2)+...+Tn-1*1+Tn*0

            要让接水总等待时间最小,就要让T1<T2<...<Tn

    ​        因此本题的贪心策略是:要让平均等待时间最小,就要让接水时间短的人往前排先接水。将数组按照接水时间从小到大排列。求平均等待时间时,遍历整个数组,对依次接水的每个人,将其他人在这个人接水时等待的总时间累加到sum上,最后sum除以n。

          (2)源程序。

    #include <stdio.h>

    int main()

    {

       int n;

       scanf("%d",&n);

       int t[1001],id[1001];

       int i,j,tmp;

       for (i=0;i<n;i++)

       {

          scanf("%d",&t[i]);

          id[i]=i+1;

       }

       for (i=0;i<n-1;i++)      // 按接水时间从小到大排列

         for (j=0;j<n-1-i;j++)

         {

             if (t[j]>t[j+1])

             {

                 tmp=t[j];

                 t[j]=t[j+1];

                 t[j+1]=tmp;

                 tmp=id[j];

                 id[j]=id[j+1];

                 id[j+1]=tmp;

             }

         }

       for (i=0;i<n;i++)

          printf("%d ",id[i]);

       printf("\n");

       double sum=0;

       for (i=0;i<n-1;i++)

       {

           sum+=t[i]*(n-1-i);

       }

       printf("%.2f\n",sum/n);

       return 0;

    }

    【例3】智力大冲浪。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P1230)。

    题目描述

    小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元。先不要太高兴!因为这些钱还不一定都是你的?!接下来主持人宣布了比赛规则:

    首先,比赛时间分为n个时段(n≤500),它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成(1≤ti≤n)。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱!注意:比赛绝对不会让参赛者赔钱!

    输入格式

    第1行为m,表示一开始奖励给每位参赛者的钱;

    第2行为n,表示有n个小游戏;

    第3行有n个数,分别表示游戏1到n的规定完成期限;

    第4行有n个数,分别表示游戏1到n不能在规定期限前完成的扣款数。

    输出格式

    仅1行。表示小伟能赢取最多的钱。

    输入样例

    10000

    7

    4 2 4 3 1 4 6

    70 60 50 40 30 20 10

    输出样例

    9950

            (1)编程思路。

            本题用贪心算法求解。因为不同的小游戏不能准时完成时具有不同的扣款数,因此贪心策略是让扣款数额大的尽量在规定的期限内完成,这样可先把这些游戏按照扣款的数额从大到小进行排列。然后按排序好的游戏任务进行安排,即先安排扣款数大的游戏。

            每个游戏安排玩的时间段时,若其完成期限是k,就将它安排在小于等于k的最靠后的空闲时间段。一旦出现一个不可能在规定期限内完成的游戏,则把其安排到最后的一个空时间段,因为不能完成的游戏在任意一个时间段中扣款数额都是一样的,这样得到的结果必然是最优的。

           (2)源程序。

    #include <stdio.h>

    struct node

    {

        int w,t;

    };

    int main()

    {

        int m,n;

        scanf("%d%d",&m,&n);

        struct node a[501],temp;

        int i,j,k;

        for (i=0;i<n;i++)

            scanf("%d",&a[i].t);

        for (i=0;i<n;i++)

            scanf("%d",&a[i].w);

        for (i=0;i<n-1;i++)

           for (j=0;j<n-1-i;j++)

              if (a[j].w<a[j+1].w)

              {

                 temp=a[j];  a[j]=a[j+1];  a[j+1]=temp;

              }

        int b[501]={0};            // b[i]=1表示时段i已被某游戏占用

        int ans=m;

        for (i=0;i<n;i++)

        {

                       for (j=a[i].t;j>=1;j--)      //  安排在小于等于k的最靠后的空闲时间段

                       {

                                if(b[j]==0)

                                {

                                         b[j]=1;

                                         break;

                                }

                       }

                       if (j<1)                 // 没法完成,罚款吧

                       {

                                for (k=n;k>=1;k--)    // 安排到最后的一个空时间段

                                {

                                         if (b[k]==0)

                                         {

                                                   b[k]=1;

                                                   break;

                                         }

                                }

                                ans-=a[i].w;

                       }

             }

        printf("%d\n",ans);

           return 0;

    }

     【例4】种树。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/P1250)。

    题目描述

    一条街的一边有几座房子,因为环保原因居民想要在路边种些树。

    路边的地区被分割成块,并被编号成 1,2,…,n。每个部分为一个单位尺寸大小并最多可种一棵树。

    每个居民都想在门前种些树,并指定了三个号码 b,e,t。这三个数表示该居民想在地区b 和 e 之间(包括 b 和 e)种至少 t 棵树。

    居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。

    输入格式

    输入的第一行是一个整数,代表区域的个数 n(1≤n≤3×104)。

    输入的第二行是一个整数,代表房子个数 h(1≤h≤5×103)。

    第 3 到第 (h+2) 行,每行三个整数,第 (i+2) 行的整数依次为 bi、ei、 ti,代表第 i 个居民想在bi 和 ei 之间种至少 ti 棵树。

    输出格式

    输出一行一个整数,代表最少的树木个数。

    输入样例

    9

    4

    1 4 2

    4 6 2

    8 9 2

    3 5 2

    输出样例

    5

            (1)编程思路。

            本题采用贪心法求解。

            由于居民想种的树的各自区域可以相交,为求出能够满足所有居民的要求所需要种树的最少数量,就需要尽量在相交的部分种树,这样可以使种树的数量最少。由于每位居民与上一位居民的相交处位于上一位居民的尾部,因此可以考虑从尾部开始种树。

            具体的贪心策略为:先按照每户居民的结束位置从小到大排列,如果结束位置相等,则按照开始位置从大到小排列。然后用一个数组记录各位置点有没有种树。种树时,对排序好的每位居民的要求,从后往前种,即尽量在每个居民要求的交集处种树,这样该树可以为多个居民需求共用,最后种的树也将是最少的。

            (2)源程序。

    #include <stdio.h>

    struct node

    {

        int b,e,t;

    };

    int main()

    {

        int n,h;

        scanf("%d%d",&n,&h);

        struct node a[5001],temp;

        int i,j;

        for (i=0;i<h;i++)

            scanf("%d%d%d",&a[i].b,&a[i].e,&a[i].t);

        for (i=0;i<h-1;i++)

           for (j=0;j<h-1-i;j++)

              if (a[j].e>a[j+1].e || (a[j].e==a[j+1].e && a[j].b<a[j+1].b))

              {

                 temp=a[j];  a[j]=a[j+1];  a[j+1]=temp;

              }

        int b[30001]={0};       // b[i]=1表示位置i已种树

        int ans=0;

        for (i=0;i<h;i++)

        {

            int sum=0;

            for (j=a[i].b;j<=a[i].e;j++)

                sum+=b[j];   // 累加在这段区间上已经种的树的数目

            for (j=a[i].e;j>=a[i].b && sum<a[i].t;j--)

            {

                if (b[j]!=1)

                {

                    b[j]=1;

                    sum++;

                    ans++;

                }

            }

        }

        printf("%d\n",ans);

           return 0;

    }

            注:这段代码提交给P1986 元旦晚会(https://www.luogu.com.cn/problem/ P1986)同样可以Accepted。

    【例5】County Fair Events S。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P6244)。

    题目描述

    FJ 参加活动。

    他想参加尽可能多的 N 个活动,参加完某个之后可以立刻参加下一个。

    给定 FJ 可参加的活动列表、其开始时间 T 和持续时间 L ,求 FJ 可以参加的最大活动数。

    FJ 每个活动都不会提早离开。

    输入格式

    第一行有一个整数 N(1≤N≤104)。

    第二到 N+1 行:每行包含两个用空格分隔的整数 T 和 L (1≤T,L≤105),意义如上述。

    输出格式

    输出仅一行,FJ 最多能参加几个活动。

    输入样例

    7

    1 6

    8 6

    14 5

    19 2

    1 8

    18 3

    10 6

    输出样例

    4

            (1)编程思路。

            贪心策略为:在可选的活动中,每次选取结束时间最早的活动参加。这样之后可能选择参加的活动就更多些。

          (2)源程序。

    #include <stdio.h>

    struct Event

    {

        int begin,end;

    };

    int main()

    {

        int n;

        scanf("%d",&n);

        struct Event a[10001],tmp;

        int i,j;

        for (i=0;i<n;i++)

        {

            int t,l;

            scanf("%d%d",&t,&l);

            a[i].begin=t;

            a[i].end=t+l;

        }

        for (i=0;i<n-1;i++)    // 将各活动按结束时间从早到晚排列

          for (j=0;j<n-1-i;j++)

          {

              if (a[j].end>a[j+1].end)

              {

                   tmp=a[j];  a[j]=a[j+1]; a[j+1]=tmp;

              }

          }

        int cnt=1;

        int finish=a[0].end;

        for (i=1;i<n;i++)

        {

            if (a[i].begin>=finish)

            {

                cnt++;

                finish=a[i].end;

            }

        }

        printf("%d\n",cnt);

        return 0;

    }

     【例6】泥泞路。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P1589)。

    题目描述

    暴雨过后,FJ的农场到镇上的公路上有一些泥泞路,他有若干块长度为L的木板可以铺在这些泥泞路上,问他至少需要多少块木板,才能把所有的泥泞路覆盖住。

    输入格式

    第一行为正整数n(≤10000)和L(≤10000),分别表示有多少段泥泞路和木板的长度;接下来n行,每一行两个整数s和e(s≤e≤10^9),表示每一段泥泞路的起点和终点。

    输出格式

    仅一个正整数,表示木板数。

    输入样例

    3 3

    1 6

    13 17

    8 12

    输出样例

    5

            (1)编程思路。

            将每段泥泞路按起点从小到大排列。定义变量begin表示铺木板的人行走到的位置,初始值为0。

            遍历排序好的泥泞路数组,对于每段泥泞路,若begin<s[i],表示铺木板的人所在位置与泥泞路之间不存在泥泞,无需铺木板,直接走到这段泥泞路的起点,即begin=s[i],之后开始铺木板,每铺一块木板,铺木板人向前走L,即begin=begin+L并计数所铺木板块数,直到铺木板人的位置到达或超过了这段泥泞路的终点(begin≥e[i])。

           (2)源程序。

    #include <stdio.h>

    int main()

    {

       int n,l;

       scanf("%d%d",&n,&l);

       int s[10001],e[10001];

       int i,j,t;

       for (i=1;i<=n;i++)

           scanf("%d%d",&s[i],&e[i]);

       for (i=1;i<n;i++)

         for (j=1;j<=n-i;j++)

            if (s[j]>s[j+1])

            {

               t=s[j];  s[j]=s[j+1];  s[j+1]=t;

               t=e[j];  e[j]=e[j+1];  e[j+1]=t;

            }

       int ans=0;

       int begin=0;

       for (i=1;i<=n;i++)

       {

           if (begin<s[i]) begin=s[i];

           while (begin<e[i])

           {

               ans++;

               begin+=l;

           }

       }

       printf("%d\n",ans);

       return 0;

    }

     【例7】营养膳食。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P2095)。

    题目描述

    Mr.L 正在完成自己的增肥计划。

    为了增肥,Mr.L 希望吃到更多的脂肪。然而也不能只吃高脂肪食品,那样的话就会导致缺少其他营养。

    Mr.L 通过研究发现:真正的营养膳食规定某类食品不宜一次性吃超过若干份。比如就一顿饭来说,肉类不宜吃超过 11 份,鱼类不宜吃超过 11 份,蛋类不宜吃超过 11 份,蔬菜类不宜吃超过 22 份。

    Mr.L 想要在营养膳食的情况下吃到更多的脂肪,当然 Mr.L 的食量也是有限的。

    输入格式

    第一行包含三个正整数 n,m和 k。表示 Mr.L 每顿饭最多可以吃 m 份食品,同时有 n 种食品供 Mr.L 选择,而这 n 种食品分为 k 类。

    第二行包含 k 个不超过 10的正整数,表示可以吃 1 到 k 类食品的最大份数。

    接下来 n 行每行包括 2 个正整数,分别表示该食品的脂肪指数 ai和所属的类别 bi。

    输出格式

    包括一个数字即 Mr.L 可以吃到的最大脂肪指数和。

    输入样例

    6 6 3

    3 3 2

    15 1

    15 2

    10 2

    15 2

    10 2

    5 3

    输出样例

    60

           (1)编程思路。

            贪心策略是优先吃脂肪指数高的食品。因此将所有食品按脂肪指数从大到小排列即可。

           (2)源程序。

    #include <stdio.h>

    struct Food

    {

             int a,b;

    };

    int main()

    {

             int n,m,k;

             scanf("%d%d%d",&n,&m,&k);

             int i,j,b[101];

             for (i=1;i<=k;i++)

                       scanf("%d",&b[i]);

             struct Food f[201],t;

             for (i=1;i<=n;i++)

                       scanf("%d%d",&f[i].a,&f[i].b);

             for (i=1;i<n;i++)  // 食物按脂肪含量从大到小排列

               for (j=1;j<=n-i;j++)

                  if (f[j].a<f[j+1].a)

                 {

                    t=f[j]; f[j]=f[j+1]; f[j+1]=t;

                 }

             int ans=0;

             for (i=1;i<=n;i++)

             {

                       if (b[f[i].b]>0 && m>0)  // 还没超过这一类和总共规定

                       {

                                b[f[i].b]--;

                                m--;

                                ans+=f[i].a;

            }

             }

             printf("%d",ans);

             return 0;

    }

     【例8】合并果子。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P1090)。

    题目描述

    在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

    每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

    因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

    例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12。所以多多总共耗费体力 =3+12=15。可以证明 15 为最小的体力耗费值。

    输入格式

    共两行。

    第一行是一个整数 n (1≤n≤10000) ,表示果子的种类数。

    第二行包含 n 个整数,用空格分隔,第 i 个整数 ai (1≤ai≤20000) 是第i 种果子的数目。

    输出格式

    一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231

    输入样例

    3

    1 2 9

    输出样例

    15

          (1)编程思路。

            贪心策略为:每次合并当前各果子堆中果子数最少的两堆果子,直到所有果子合并为一堆。

          (2)源程序。

    #include <stdio.h>

    int main()

    {

       int n;

       scanf("%d",&n);

       int a[10001];

       int i,j,t;

       for (i=0;i<n;i++)

           scanf("%d",&a[i]);

       for (i=0;i<n-1;i++)

         for (j=0;j<n-1-i;j++)

         {

             if (a[j]>a[j+1])

             {

                 t=a[j];

                 a[j]=a[j+1];

                 a[j+1]=t;

             }

         }

       int ans=0;

       for (i=0;i<n-1;i++)

       {

           t=a[i]+a[i+1];

           ans+=t;

           for (j=i+1;j<n-1;j++)

              if (a[j+1]<t) a[j]=a[j+1];

              else break;

           a[j]=t;

       }

       printf("%d\n",ans);

       return 0;

    }

     【例9】运输。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P2094)

    题目描述

    现在已知 N 件商品,和搬运它们其中每一件的费用。现在搬家公司老板 Mr.sb 决定让我们每次任意选取 2 件商品。然后这 2 件商品只算一件商品的费用。但是这个商品的搬运费用是将选出的 2 个商品的费用之和除以 $k 的运算结果。如此反复。直到只收一件商品的钱。这个就是商店要付的费用。掌柜的想尽可能的少付钱,以便将更多的钱捐给希望工程。所以请你帮他计算一下最少只用付多少钱。

    输入格式

    第一行两个整数 n,k。

    第二行 n 个整数w1 ,w2,…,wn,表示每一件物品搬运费。

    输出格式

    一行一个整数表示最少付多少钱。

    输入样例

    5 2

    1 2 3 4 5

    输出样例

    1

            (1)编程思路。

            贪心策略为:每次选取当前未组合商品中搬运费最多的两件商品进行组合计价,使得搬运费高的商品尽可能多的被除以k。

            (2)源程序。

    #include <stdio.h>

    int main()

    {

       int n,k;

       scanf("%d%d",&n,&k);

       int a[10001];

       int i,j,t;

       for (i=0;i<n;i++)

           scanf("%d",&a[i]);

       for (i=0;i<n-1;i++)

         for (j=0;j<n-1-i;j++)

         {

             if (a[j]<a[j+1])

             {

                 t=a[j];

                 a[j]=a[j+1];

                 a[j+1]=t;

             }

         }

       for (i=0;i<n-1;i++)

       {

           t=(a[i]+a[i+1])/k;

           for (j=i+1;j<n-1;j++)

              if (a[j+1]>t) a[j]=a[j+1];

              else break;

           a[j]=t;

       }

       printf("%d\n",t);

       return 0;

    }

     【例10】母舰。

            本题选自洛谷题库(https://www.luogu.com.cn/problem/ P2813)。

    题目描述

    在小A的星际大战游戏中,一艘强力的母舰往往决定了一场战争的胜负。一艘母舰的攻击力是普通的MA(Mobile Armor)无法比较的。

    对于一艘母舰而言,它是由若干个攻击系统和若干个防御系统组成的。两艘母舰对决时,一艘母舰会选择用不同的攻击系统去攻击对面母舰的防御系统。当这个攻击系统的攻击力大于防御系统的防御力时,那个防御系统会被破坏掉。当一艘母舰的防御系统全部被破坏掉之后,所有的攻击都会攻击到敌方母舰本身上去造成伤害。

    这样说,一艘母舰对对面的伤害在一定程度上是取决于选择的攻击对象的。

    在瞬息万变的战场中,选择一个最优的攻击对象是非常重要的。所以需要写出一个战斗系统出来,判断出你的母舰最多能对对手造成多少伤害并加以实现。

    输入格式

    输入第一行两个整数M和N,表示对方母舰的防御系统数量和你的母舰的攻击系统数量。

    接着M行每行一个整数每一个表示对方防御系统的防御力是多少。

    接着N行每行一个整数每一个表示己方攻击系统的攻击力是多少。

    输出格式

    输出仅有一行,表示可以造成的最大伤害。

    输入样例

    3 5

    1000

    2000

    1200

    2100

    2000

    1200

    1000

    1000

    输出样例

    2000

           (1)编程思路。

           本题的贪心策略为:在能够打破敌方的防御系统的我方攻击系统中,用尽量小的攻击系统来打敌方的防御系统,这样好留下大的攻击系统来打母舰以造成更大的伤害。为此,将攻击力数组和防御力数组分别按从小到大的顺序排列。

           (2)源程序。

    #include <stdio.h>

    #include <algorithm>

    using namespace std;

    int main()

    {

        int defense[100001],attack[100001],i;

        int m,n;

        scanf("%d%d",&m,&n);

        for (i=0;i<m;i++)

            scanf("%d",&defense[i]);

        for (i=0;i<n;i++)

            scanf("%d",&attack[i]);

        sort(defense,defense+m);

        sort(attack,attack+n);

        int k=0;  // 敌方现有的最小的防御系统

        for(i=0;i<n;i++)

        {

           if (defense[k]==0) k++;

           else if (defense[k]<attack[i])  // 当前的攻击系统能打破敌方现有的最小的防御系统

           {

               attack[i]=0;

               k++;

           }

        }

        if (k<m)    // 打不完防御系统就一点伤害都没有

            printf("0\n");

        else

        {

            int ans=0;

            for (i=0;i<n;i++) ans+=attack[i];

            printf("%d\n",ans);

        }

        return 0;

    }

             在理解了贪心法及其应用方法的基础上,可以刷一下如下的洛谷OJ题目。这几道题目均可以采用贪心法解决。

            (1)P1208 [USACO1.3]混合牛奶 Mixing Milk(https://www.luogu.com.cn/problem/ P1208)。

    #include <stdio.h>
    struct Milk
    {
        int p,a;
    };
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        struct Milk a[5001];
        int i,j;
        for (i=0;i<m;i++)
            scanf("%d%d",&a[i].p,&a[i].a);
        for (i=0;i<m-1;i++)
            for (j=0;j<m-1-i;j++)
            {
               if (a[j].p>a[j+1].p)
               {
                   struct Milk temp;
                   temp=a[j]; a[j]=a[j+1]; a[j+1]=temp;
               }
            }
        int ans=0;
        for (i=0;i<n;i++)
        {
            if (n>a[i].a)
            {
                ans+=a[i].p*a[i].a;
                n-=a[i].a;
            }
            else
            {
                ans+=a[i].p*n;
                break;
            }
        }
        printf("%d\n",ans);
        return 0;
    }
    P1208 参考源程序

            (2)P1209 [USACO1.3]修理牛棚 Barn Repair(https://www.luogu.com.cn/problem/ P1209)。

    #include <stdio.h>
    int main()
    {
       int m,s,c;
       scanf("%d%d%d",&m,&s,&c);
       int a[201],b[201];
       int i,j,t;
       for (i=1;i<=c;i++)
           scanf("%d",&a[i]);
       if (m>=c)
       {
           printf("%d\n",c);
           return 0;
       }
       for (i=1;i<c;i++)
         for (j=1;j<=c-i;j++)
            if (a[j]>a[j+1])
            {
               t=a[j];  a[j]=a[j+1];  a[j+1]=t;
            }
       int ans = a[c] - a[1] + 1;
       for (i=1;i<c;i++)
           b[i]=a[i+1]-a[i];
       for (i=1;i<c-1;i++)
         for (j=1;j<c-i;j++)
            if (b[j]<b[j+1])
            {
               t=b[j];  b[j]=b[j+1];  b[j+1]=t;
            }
       for(int i=1;i<m;i++)
       {
           ans=ans-b[i]+1;
       }
       printf("%d\n",ans);
       return 0;
    }
    P1209 参考源程序

           (3)P1325 雷达安装(https://www.luogu.com.cn/problem/ P1325)。

    #include <stdio.h>
    #include <math.h>
    struct Position
    {
        int x,y;
    };
    struct Range
    {
        double left,right;
    };
    int main()
    {
        int n,d;
        scanf("%d%d",&n,&d);
        int flag=0;
        int i,j;
        struct Position pos[1001];
        struct Range dis[1001];
        for (i=0;i<n;i++)
        {
            scanf("%d%d",&pos[i].x,&pos[i].y);
            if (d>=pos[i].y)
            {
                double dd=sqrt(d*d-pos[i].y*pos[i].y);
                dis[i].left=pos[i].x-dd;
                dis[i].right=pos[i].x+dd;
            }
            else
                flag=1;
        }
        if (flag)
            printf("-1\n");
        else
        {
            for (i=0;i<n-1;i++)
                for (j=0;j<n-1-i;j++)
                   if (dis[j].right>dis[j+1].right)
                   {
                       struct Range temp;
                       temp=dis[j]; dis[j]=dis[j+1]; dis[j+1]=temp;
                   }
            int cnt=1;
            double finish=dis[0].right;
            for (i=1;i<n;i++)
            {
                if (dis[i].left>finish)
                {
                   cnt++;
                   finish=dis[i].right;
                }
            }
            printf("%d\n",cnt);
        }
        return 0;
    }
    P1325 参考源程序

          (4)P1645序列(https://www.luogu.com.cn/problem/ P1645)。

    #include <stdio.h>
    struct node
    {
        int b,e,t;
    };
    int main()
    {
        int n;
        scanf("%d",&n);
        struct node a[1001],temp;
        int i,j;
        for (i=0;i<n;i++)
            scanf("%d%d%d",&a[i].b,&a[i].e,&a[i].t);
        for (i=0;i<n-1;i++)
           for (j=0;j<n-1-i;j++)
              if (a[j].e>a[j+1].e || (a[j].e==a[j+1].e && a[j].b<a[j+1].b))
              {
                 temp=a[j];  a[j]=a[j+1];  a[j+1]=temp;
              }
        int b[1001]={0};
        int ans=0;
        for (i=0;i<n;i++)
        {
            int sum=0;
            for (j=a[i].b;j<=a[i].e;j++)
                sum+=b[j];
            for (j=a[i].e;j>=a[i].b && sum<a[i].t;j--)
            {
                if (b[j]!=1)
                {
                    b[j]=1;
                    sum++;
                    ans++;
                }
            }
        }
        printf("%d\n",ans);
          return 0;
    }
    P1645 参考源程序

          (5)P1803 凌乱的yyy / 线段覆盖(https://www.luogu.com.cn/problem/ P1803)。

    #include <stdio.h>
    #include <algorithm>
    using namespace std;
    struct Match
    {
        int begin,end;
    };
    struct Match a[1000001];
    int cmp(struct Match a,struct Match b)
    {
        return a.end<b.end;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
    
        int i,j;
        for (i=0;i<n;i++)
          scanf("%d%d",&a[i].begin,&a[i].end);
        sort(a,a+n,cmp);
        int cnt=1;
        int finish=a[0].end;
        for (i=1;i<n;i++)
        {
            if (a[i].begin>=finish)
            {
                cnt++;
                finish=a[i].end;
            }
        }
        printf("%d\n",cnt);
        return 0;
    }
    P1803 参考源程序

          (6)P1842 [USACO05NOV]奶牛玩杂技(https://www.luogu.com.cn/problem/ P1842)。

    #include <stdio.h>
    #include <algorithm>
    using namespace std;
    struct node
    {
        int w,s;
    };
    int cmp(struct node a,struct node b)
    {
        return a.w+a.s<b.w+b.s;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        struct node cow[50001],t;
        int i,j;
        for (i=0;i<n;i++)
            scanf("%d%d",&cow[i].w,&cow[i].s);
        sort(cow,cow+n,cmp);
        int ans=-2000000000;
        int sum=0;
        for (i=0;i<n;i++)
        {
            if (sum-cow[i].s>ans) ans=sum-cow[i].s;
            sum+=cow[i].w;
        }
        printf("%d\n",ans);
        return 0;
    }
    P1842 参考源程序

          (7)P2242 公路维修问题(https://www.luogu.com.cn/problem/ P2242)。

    #include <stdio.h>
    int main()
    {
       int n,m;
       scanf("%d%d",&n,&m);
       int a[15001],b[15001];
       int i,j,t;
       for (i=1;i<=n;i++)
           scanf("%d",&a[i]);
       int ans = a[n] - a[1] + 1;
       for (i=1;i<n;i++)       //计算出每两个坑间的距离
           b[i]=a[i+1]-a[i];
       for (i=1;i<n-1;i++)
         for (j=1;j<n-1;j++)
            if (b[j]<b[j+1])
            {
               t=b[j];  b[j]=b[j+1];  b[j+1]=t;
            }
       for(int i=1;i<m;i++)
       {
           ans=ans-b[i]+1;
       }
       printf("%d\n",ans);
       return 0;
    }
    P2242 参考源程序

          (8)P2945 [USACO09MAR]Sand Castle S(https://www.luogu.com.cn/problem/ P2945)。

    #include <stdio.h>
    #include <algorithm>
    using namespace std;
    int main()
    {
        int a[25001],b[25001],i,j;
        int n,x,y;
        scanf("%d%d%d",&n,&x,&y);
        for (i=0;i<n;i++)
            scanf("%d%d",&a[i],&b[i]);
        sort(a,a+n);
        sort(b,b+n);
        int ans=0;
        for (i=0;i<n;i++)
            if (a[i]>b[i]) ans+=(a[i]-b[i])*y;
            else ans+=(b[i]-a[i])*x;
        printf("%d\n",ans);
        return 0;
    }
    P2945 参考源程序

          (9)P3143 [USACO16OPEN]Diamond Collector S(https://www.luogu.com.cn/problem/ P3143)。

    #include <stdio.h>
    #include <algorithm>
    using namespace std;
    int max(int a,int b)
    {
        return a>b?a:b;
    }
    int main(void)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        int a[50001];
        int i,j;
        for(i=1;i<=n;i++)
          scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        int pos=1;
        int left[50001]={0},right[50001]={0};
        for(i=1;i<=n;i++)
        {
           while (a[i]-a[pos]>k) pos++;
           left[i]=max(left[i-1],i-pos+1);
        }
        pos=n;
        for(i=n;i>=1;i--)
        {
           while (a[pos]-a[i]>k) pos--;
           right[i]=max(right[i+1],pos-i+1);
        }
        int ans=0;
        for(i=1;i<n;i++)
        {
            ans=max(ans,left[i]+right[i+1]);
        }
        printf("%d\n",ans);
        return 0;
    }
    P3143 参考源程序

          (10)P4995 跳跳!(https://www.luogu.com.cn/problem/ P4995)。

    #include <stdio.h>
    int main()
    {
       int n;
       scanf("%d",&n);
       int a[301];
       int i,j,t;
       for (i=0;i<n;i++)
          scanf("%d",&a[i]);
       for (i=0;i<n-1;i++)
         for (j=0;j<n-1-i;j++)
           if (a[j]<a[j+1])
           {
              t=a[j]; a[j]=a[j+1]; a[j+1]=t;
           }
       long long ans=1ll*a[0]*a[0];
       int left=1;
       int right=n-1;
       while (left<=right)
       {
           ans+=1ll*(a[left-1]-a[right])*(a[left-1]-a[right]);
           right--;
           if (left<=right)
             ans+=1ll*(a[left]-a[right+1])*(a[left]-a[right+1]);
           left++;
       }
       printf("%lld\n",ans);
       return 0;
    }
    P4995 参考源程序
  • 相关阅读:
    【分治】洛谷试炼场
    【Manacher】Colorful String
    【动态规划】背包九讲及相应习题
    【算法课】最大间隙问题
    【hash】Similarity of Subtrees
    YBT 股票买卖
    YBT 鸡蛋的硬度
    YBT 电池的寿命
    YBT Ride to Office
    YBT 装箱问题
  • 原文地址:https://www.cnblogs.com/cs-whut/p/15514999.html
Copyright © 2011-2022 走看看