《训练指南》——6.13

  困于时间缘故和考试缠身,笔者在先前关于《训练指南》的而第二章的数学基础的介绍先告一段落,开始对第一章简单的一些算法基础题目进行介绍。

  Uva11292:

  你的王国里有一条n个头的恶龙,你希望雇一些其实把它杀死(即砍掉所有的头)。村里有m个其实可以雇佣,一个能力值为x的其实可以砍掉恶龙一个半径不超过x的头,且需要支付x个金币。如何雇佣其实才能砍掉恶龙的所有头,且需要支付的金币最少?注意,一个其实只能看一个头(且只能被雇用一次)。

  分析:观察到题目给出支付最少的字眼,我们容易将其往动态规划或者贪心的算法思想上靠拢,而如果有一定动态规划基础的读者,可能会和笔者一样,认为它非常近似01背包的模型。

  那么我们继续对比一下两个模型参量的区别看是否能够建立起联系,对于我们熟悉的01背包模型,即有n个价值、体积已知的物体,装入一个容积为v的背包,我们能够找到使得价值之和最大的方案,其主要的特征就是有n个物体,每个物体两个维度的参量(价值、体积),然后给出一个参量的限制(背包的体积,进一步抽象这个参量,可将其理解为费用),求解另一个参量的最值。

  那么我们反过来看这道题目,m个人,有两个维度的参量:费用和能力。而对于n头龙,显然明显与01背包中容积为v的背包有着本质的区别,它会限制对勇士的选取,即不像01背包当中任何一个物体都可以放入背包,因此这里如果用01背包处理,仿佛并不是那么容易。

  其实上面这个过程刻意将这个问题的思考复杂化了,其实这道问题很简单,它其实就基于一种最简单的贪心策略,然后会用到分而治之当中所谓“表示变更”(《算法谜题》当中有过介绍),我们只需要对m个勇士的能力进行从小到大排序,然后从能力较小者依次往后选取能够砍掉一个头的勇士即可。

  简单的参考代码如下:

  #include<cstdio>

#include<algorithm>

#include<cstring>

using namespace std;

const int maxn = 20005;

int main()

{

     int boss[maxn],fighter[maxn];

     int n , m;

     while(scanf("%d %d",&n,&m) && (m || n))

     {

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

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

         for(int i = 1;i <= m;i++)

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

         sort(boss + 1 , boss + n + 1);

         sort(fighter + 1 , fighter + m + 1);

 

 

        int index = 1;

        int sum = 0;

        for(int i = 1;i <= m;i++)

        {

                for(int j = index;j <= n;j++)

                {

                    if(fighter[i] >= boss[j])

                       {

                           index++;

                           sum += fighter[i];

                            break;

                       }

 

                }

                if(index == n + 1)

                      break;

        }

        if(index == n + 1)

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

        else

              printf("Loowater is doomed!\n");

 

     }

}

 

 

Uva 11729:

  你有n个部下,每个部下需要完成一项任务。第i个部下需要你花Bi分钟交代任务,然后他会立刻独立的、无间断地执行Ji分钟后完成任务。你需要选择交代任务的顺序,是的所有任务今早执行完毕。注意,不能同时给两个部下交代任务,但部下们可以同时执行他们各自的任务。那么请问对于每组数据,输出完成任务的最短时间。

  分析:基于一个时间区段上去思考这个问题。每个人所占有的时间区段分成两部分,交代任务和做任务。由于题设的限制,交代任务的区段是不可能重合的,那么这里我们考虑贪心策略,显然是让做任务区段最长的先去做任务,我们整个过程表述成如下的伪代码:

  定义cost_time[i]是完成i个任务所有的最少时间

  for i 1 to num_of_person

      cost_time[i] = max(cost_time[i-1] , cost_time[i-1] – Ji[i-1]+ J[i] + B[i])

  简单的参考代码如下(思路很清晰但是就是A不了,测了多组数据也能过,想扔在这里吧):

#include<cstdio>

#include<algorithm>

using namespace std;

const int maxn  = 1005;

 

struct person

{

    int B , J;

};

 

bool cmp(person a , person b)

{

     return a.J >= b.J;

}

int main()

{

    struct person p[maxn];

    int n;

    int tt = 1;

    while(scanf("%d",&n) && n != 0)

    {

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

         {

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

         }

         sort(p + 1 , p + n + 1 , cmp);

        // printf("%d %d %d",p[1].J,p[2].J,p[3].J);

 

         int cost_time = p[1].B + p[1].J;

         for(int i = 2;i <= n;i++)

         {

              cost_time = max(cost_time , cost_time - p[i-1].J + p[i].J + p[i].B);

              //  printf("%d ",cost_time);

         }

         printf("Case %d: %d\n",tt++,cost_time);

    }

}       

 

转载于:https://www.cnblogs.com/rhythmic/p/5585455.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值