贪心算法小结2

F-Ants

一队蚂蚁在一根水平杆上行走,每只蚂蚁固定速度 1cm/s. 当一只蚂蚁走到杆的尽头时,立即从秆上掉落. 当两只蚂蚁相遇时它们会掉头向相反的方向前进. 我们知道每只蚂蚁在杆上的初始位置, 但是, 我们不知道蚂蚁向哪个方向前行. 你的任务是计算所有蚂蚁都杆上掉落可能的最短时间和最长时间.

Input

第一行包含一个整数,给出测试实例数量. 每组数据开始有两个整数: 杆的长度 (单位:cm) 和杆上蚂蚁数量 n. 之后是 n 个整数给出每只蚂蚁从杆的最左边开始的位置, 且是无序的. 输入的每个整数都不大于 1000000 ,两个数字用空格分开.

Output

对于每组输入输出两个整数. 第一个整数表示所有蚂蚁从杆上掉落可能的最短时间(如果它们前行方向选择得当) ,第二个整数表示可能的最长时间.

Sample Input

2
10 3
2 6 7
214 7
11 12 7 13 176 23 191

Sample Output

4 8
38 207
  解题思路:这个题想明白一点就好写了。即蚂蚁的速度是一样的,转身不消耗时间,那么可以将两只相遇时掉头的蚂蚁,看做交错而过,不理会相遇即可。因为掉头与交错而过的时间是一样的。再暴力搜素每一个蚂蚁的位置,如果是最短时间,则比较
它距杆左边的距离与距杆右边的距离取最小,取出最大的最小值,即为所求 。如果是最长时间,比较它与杆左边的距离与杆右边的距离取最大,最后取出最大值。代码如下:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <map>
#include <stack>
#include <utility>
using namespace std;
int const max_n=500;
int a[max_n];
int main()
{
    int m,n,t;
    scanf("%d",&t);
    while(t--){
        cin>>m>>n;
        for(int i=0;i<n;i++)cin>>a[i];
        int minN=0,maxN=0;//最短时间,最长时间
        for(int i=0;i<n;i++)minN=max(minN,min(a[i],m-a[i]));
        for(int i=0;i<n;i++)maxN=max(maxN,max(a[i],m-a[i]));
        cout<<minN<<" "<<maxN<<endl;
        memset(a,0,sizeof(a));
    }
    return 0;
}

 G-Fence Repair

Description

It is universally accknowledged that 泥煤(peat)是一个非常珍贵的收藏品,越大的泥煤收藏价值越高。

一天,王泥煤找到了阿拉伯神灯,也就是阿拉丁神灯的弟弟,他向阿拉伯神灯许了一个愿望,从此获得了一个超能力,可以将两个泥煤合并为更大的泥煤。但是这个能力非常的鸡肋,王泥煤需要支付与这两块泥煤等价值的财富才能将他们合并。

比如:王泥煤把两块价值分别为3和5的泥煤合并,可以得到一块价值为8的泥煤,但是要消耗3+5的财富。

王泥煤想知道,他将手中的n块泥煤合并到只剩下一块之后,最少需要花费多少财富。

Input

第一行为一个整数n(n <= 20000),代表王泥煤拥有的泥煤数量,接下来n行,每行一个整数a_i(1 <= a_i <= 50000),代表每个泥煤的价值

Output

输出包括一行,请告诉王泥煤他需要花费的最少财富。

Sample Input

3
8
5
8

Sample Output

34
   解题思路:就是不断选取最小的两块煤进行合成,合成后放入煤堆再找两个最小的,如果取一次排序一次,时间复杂度太高o(n^2),因此要用到优先队列 ,注意使用默认的greater 按升序排序就可以了。注意数据范围,要开long long 代码如下:
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define LL long long
using namespace std;
int main()
{
    int n,x;
    LL maxq=0;
    scanf("%d",&n);
    priority_queue<LL,vector<LL>,greater<LL> >q;
    for(int i=0;i<n;i++){scanf("%d",&x);q.push(x);}
    while(q.size()!=1)
    {
        LL a,b;
        a=q.top();q.pop();
        b=q.top();q.pop();
        LL c=a+b;
        q.push(c);
        maxq+=c;
    }
    cout<<maxq<<endl;
    return 0;
}

 H-最少拦截系统

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹. 
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统. 

Input输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔) 
Output对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统. 
Sample Input

8 389 207 155 300 299 170 158 65

Sample Output

2
  解题思路:因为不能漏掉任何一个导弹,所以要从第一个导弹开始,构建一个下降子序列;而在构建下降过程中的导弹若高于上一个导弹,则必须要使用新的拦截系统。这题我一开始写的挺复杂的,过了之后看题解,发现自己想复杂了,可以开一个数组,输入一个
导弹高度 ,若高于现在所有的拦截系统的高度,则必须新开一个拦截系统;若有拦截系统能够拦下它,则更新该拦截系统的高度。参考链接:https://blog.csdn.net/sdut_jk17_zhangming/article/details/79292887
代码如下:
#include<stdio.h>

int main()
{
      int a[300] = {0},i,n,j,h,m;
      while(scanf("%d",&n) != EOF)
      {
          m = 0;
          for(i= 0;i <n;i++)
          {
              scanf("%d",&h);
              for(j = 0;j <m;j++)
              {
                  if(a[j] >= h)
                    break;
              }
              if(j <m)
                a[j] = h;
              else
              {
                  a[m] = h;
                  m++;
              }
          }
          printf("%d\n",m);
      }
      return 0;
}

  写题的过程中,有不会的是很正常的,若是自己能写过,就坚持写完,写完找找题解,了解别人的思路再与自己的思路对比,最好能学到最优解。所有做题过程中不要太过于纠结题解问题,关键在于看了有没有学会最优解(或者说相对较好的思路)。

 

J - Packets

 
一家工厂生产的产品规格分为1×1, 2×2, 3×3, 4×4, 5×5, 6×6,高都是h。工厂要把它们包在6×6×h的包装袋中。工厂想让包装数尽可能少。

Input

多组数据。每一行为一组数据。依次是1×1, 2×2, 3×3, 4×4, 5×5, 6×6的产品的个数。 输入数据由6个0结束。

Output

对于每组数据,输出包装所有产品所需最少包装袋数量

Sample Input

0 0 4 0 0 1 
7 5 1 0 0 0 
0 0 1 0 1 0
0 0 0 0 0 0 

Sample Output

2 
1
2 

   思路:这是一道二维装箱题,而装箱问题一般来说比较复杂,如果针对每一种情况进行精确算法是非常繁琐且容易出错的。在这里我用到了ceil()函数,返回大于或等于表达式值的函数来简化算法。(参考链接http://blog.csdn.net/c20190413/article/details/77396357###)首先看,4×4,5×5,6×6的箱子,很显然这三种箱子没有一个就要占用一个包装袋,6×6的箱子直接占用一个包装袋,而5×5的箱子则可以塞进去11个1×1的箱子,用1×1箱子的个数减去min(a,e*11)(a为1×1箱子个数,e为5×5箱子个数,为表述方便,依次为箱子编号a、b、c、d、e、f);对于一个4×4箱子,可以塞进2×2的箱子5个,若2×2箱子数不足时,可以补充1×1箱子;b-=min(b,d*5),若2×2箱子数不足时则有a-=min(a,4*(d*5-b))。

  然后看3×3的箱子,分四种情况,箱子数为4的倍数,除以4余3、余2、余1,3种情况,这里选余2的情况为例子阐述。这时还有若干个1×1箱子若干个2×2箱子,两个3×3箱子;分析易知,此时最多可以放入3个2×2箱子,最少放入6个1×1箱子才能把包装袋填满。首先判断2×2箱子剩余数量有没有3个,不足3个的部分,用1×1箱子补充,不足时有a-=min(a,(3-b)*4);然后进行a-=min(a,6);b-=min(b,3)。其余情况依此类推。使用min函数保证不出现负数。剩余的就是若干个1×1箱子和2×2箱子,使用ceil函数可以很快解决。代码如下:

 

#include <iostream>
#include <algorithm>
#include <math.h>
#define LL long long
int const max_n=20;
using namespace std;
int main()
{
    int a,b,c,d,e,f;
    while(1){
        cin>>a>>b>>c>>d>>e>>f;
        if(!a&&!b&&!c&&!d&&!e&&!f)break;
        int num=0;
        num=d+e+f;
        a-=min(a,e*11);//减去应补充e箱子的a箱子
        if(b<d*5)a-=min(a,4*(d*5-b));//b箱子不足时,使用a箱子
        b-=min(b,d*5);//减去补充d箱子的b箱子
        num+=ceil(c/4.0);//向上取整
        c%=4;
        if(c==1){//多余1个3×3的箱子时
            if(b<5)a-=min(a,4*(5-b));//先判断b箱子数量是否足够
            a-=min(a,7);
            b-=min(b,5);
        }
        else if(c==2){//多余2个3×3的箱子时
            if(b<3)a-=min(a,4*(3-b));
            a-=min(a,6);
            b-=min(b,3);
        }
        else if(c==3)//多余3个3×3的箱子时
        {
            if(!b)a-=min(a,4);
            a-=min(a,5);
            b-=min(b,1);
        }
        num+=ceil(b/9.0);
        b%=9;
        if(b)a-=min(a,(9-b)*4);
        num+=ceil(a/36.0);
        printf("%d\n",num);
    }
    return 0;
}

 

N - Stall Reservations

 
这里有N只 (1 <= N <= 50,000) 挑剔的奶牛! 他们如此挑剔以致于必须在[A,B ]的时间内产奶(1 <= A <= B <= 1,000,000)当然, FJ必须为他们创造一个决定挤奶时间的系统.当然,没有牛想与其他奶牛分享这一时光 

帮助FJ做以下事:
  • 使每只牛都有专属时间的最小牛棚数
  • 每只牛在哪个牛棚
也许有很多可行解。输出一种即可,采用SPJ
Input
第一行一个数字 N 

第 2..N+1行: 第 i+1行 描述了i号奶牛挤奶的起止时间
Output
第一行:牛棚最小数量 

Lines 2..N+1: 第 i+1行 描述了i奶牛被安排的牛棚
Sample Input
5
1 10
2 4
3 6
5 8
4 7
Sample Output
4
1
2
3
2
4
Hint
样例解释: 

这里是一种图示 

Time     1  2  3  4  5  6  7  8  9 10

Stall 1 c1>>>>>>>>>>>>>>>>>>>>>>>>>>>
Stall 2 .. c2>>>>>> c4>>>>>>>>> .. ..
Stall 3 .. .. c3>>>>>>>>> .. .. .. ..
Stall 4 .. .. .. c5>>>>>>>>> .. .. ..
其他的也是可能的

   思路:这是一道牛奶分栏问题,分栏不算难,难点在于如何描述某只奶牛在哪个牛棚中。有两种贪心策略,一:以最早开始的奶牛进行计算,在该奶牛产奶结束后

选取离这个结束时间最近且的奶牛,安排其进行产奶,直至某个牛的结束时间大于或等于规定时间,开始为下一个牛棚安排;这种策略实际上是以牛棚为出发点,通过使用最少的牛棚让更多的牛产奶,直到所有奶牛都产过奶了;实现要用set或数组,进行一个牛棚的安排后,要对在该牛棚产奶的牛进行删除。二:将奶牛以产奶时间的早晚进行排列,在时间顺序上让每一个奶牛在产奶时间都能有牛棚产奶,若有空闲牛棚,则安排改产奶的奶牛去产奶,若是没有空闲牛棚,就要准备新的牛棚;通过优先队列以及对队列的维护实现。这里我是用了优先队列,实现代码如下:

#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#define LL long long
int const max_n=50002;
using namespace std;
struct Node{
    int st,ed,id,stal;//开始时间,结束时间,奶牛编号,牛棚号
    bool operator <(const Node &a)const{//这是优先队列中一个排序方式,重载<用来排序,维护优先队列
        return a.ed<ed;    
    }
}cow[max_n];
bool cmp(Node a,Node b)//结构体比较函数
{
    if(a.st!=b.st)
        return a.st<b.st;
    return a.ed<b.ed;
}
int main()
{
    int n,a[max_n];
    scanf("%d",&n);
    priority_queue<Node> q;
        //下标由1开始,方便计算和思考
    for(int i=1;i<=n;i++){scanf("%d %d",&cow[i].st,&cow[i].ed);cow[i].id=i;}
    sort(cow+1,cow+n+1,cmp);
    cow[0].stal=1,cow[0].ed=0;
    q.push(cow[0]);
    int i=1,k=2;//初始化牛棚数为2
    while(i<=n)
    {
        Node c=q.top();
        if(cow[i].st>c.ed)//当某个牛的开始产奶时间大于队列中最早结束时间,即这个牛产奶开始时有空闲牛棚
        {
            q.pop();
            cow[i].stal=c.stal;
            a[cow[i].id]=c.stal;
            q.push(cow[i]);
        }
        else{//没有空闲牛棚,安排新牛棚
            cow[i].stal=k;
            a[cow[i].id]=k++;
            q.push(cow[i]);
        }
        i++;
    }
    printf("%d\n",k-1);
    for(int i=1;i<=n;i++)printf("%d\n",a[i]);
    return 0;
}

 

 

O - Yogurt factory

奶牛们收购了一家世界著名的酸奶工厂Yucky Yogurt. 在接下来的 N (1 <= N <= 10,000) 周,牛奶和人工的价格每周会波动,以致于第i周需要花公司 C_i (1 <= C_i <= 5,000) 美分来生产一个单位的酸奶. Yucky factory被奶牛们照顾得很好,所以每周可以生产很多单位的酸奶 

Yucky Yogurt 拥有一个仓库,可以以S (1 <= S <= 100)美分每单位每周的价格储存没用的酸奶。神奇的是,酸奶不会变质。而且仓库十分巨大,可以容纳很多牛奶 

Yucky Yogurt每周要交货 Y_i (0 <= Y_i <= 10,000) 单位的酸奶给它的客户。请你帮助奶牛们减少整个 N-week 期间的支出. i周生产的牛奶和之前储存的牛奶都可以用来交i周的货

Input

* 第一行:N and S. 

* 第 2..N+1行:第 i+1 行包括 : C_i 和 Y_i.

Output

* 满足客户需求的最小花费,保证不超过64位整数

Sample Input

4 5
88 200
89 400
97 300
91 500

Sample Output

126900

Hint

输出提示: 
第一周生产200单位,全部售出。第二周生产700单位,售出400,储存300.第三周使用储存的300单位。第四周,生产500单位并全部售出 
注释:
yucky意为难以下咽的

 

   思路:贪心策略为,在本周的人工成本较低下周时(即生产下周要交货的奶加上存储的钱仍比下周生产牛奶价格低时),在本周多生产下周要交货的牛奶数,并存在仓库里。其他情况,就老老实实生产足够本周交货的奶,因为一般情况下不考虑生产后两周的牛奶加上存两周牛奶的钱比下下周的生产成本低的情况。代码如下:

#include <iostream>
#include <algorithm>
#define LL long long
int const max_n=10002;
using namespace std;
struct node{
    int c,y;
}num[max_n];
int main()
{
    int n,s;
    
    scanf("%d %d",&n,&s);
    for(int i=0;i<n;i++)scanf("%d %d",&num[i].c,&num[i].y);
    int i=0;
    LL mony=0;
    while(i<n)
    {
        if(num[i].c*num[i+1].y+s*num[i+1].y<num[i+1].c*num[i+1].y)
        {
            mony+=num[i].c*num[i+1].y+s*num[i+1].y;
            num[i+1].y=0;
        }
        mony+=num[i].c*num[i].y;
        i++;
    }
    cout<<mony;
    return 0;
} 

 

 

转载于:https://www.cnblogs.com/whocarethat/p/11015649.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值