几个贪心和递归的练习题

刷贪心!刷递归!

今天一天刷了四个题,效率低下,不在状态。

洛谷P1090卡了我好长时间,也算一道隔夜题,昨天晚上就已经把大致的代码写好了,可是只能过一个点。

今天早上改了半个小时,没改出来。蒟蒻的我找到了神犇@CYC(感谢@CYC对我这个小蒟蒻的支持!)

先上题目!

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。例如有 3堆果子,数目依次为 1, 2, 9。可以先将 11、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明15为最小的体力耗费值。

输入格式

共两行。
第一行是一个整数 n(1n10000) ,表示果子的种类数。第二行包含 n 个整数,用空格分隔,第 个整数 a_i(1<= a_i<=20000) 是第 i 堆果子的数目。

输出格式

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


我没有过的具体原因是我没有把合并后得到的新果堆重新排序。(原来如此!!!)
改代码!
我把代码改成合并后重新sort()排序,然后成功TLE了。。。
好气啊!
再次请教@CYC大佬!
然后,他让我把数组中元素前移两个单位,然后找一个位置,把新果堆插入进去,使数组呈升序排列。然后后面的果堆前移一个单位,形成新的数组,重复到数组只剩一个元素。
妙啊!
然后我思如泉涌势如破竹的抄了@CYC大佬的代码
仔细看了一看,并加了注释。
上代码!

#include<iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int n,a[11000];
int ans=0;//答案总数
int main()
{
cin>>n;
for(int i= 0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);//排序
int r=n-1;//r是数组中最后一个数
while(1)
{
ans+=a[0]+a[1];
int aa=a[0]+a[1];//aa是合并最小的两堆果子需要的体力
int i=0;
for(i=0;a[i+2]<aa&&i<=r-2;i++)//当a[i+2]小于aa时,把a[i+2]前移两位(因为合并了两堆果子)
{
a[i]=a[i+2];
}
a[i] = aa;//把合并的果子插入到a[i]中,这样使新数组还是呈升序排列
for(i++;i<=r;i++)//因为插入了新数a[i],a[i]后的数前移一位
{
a[i] = a[i+1];
}
r--;//因为少了一个数,所以总数--(最后一个数的位置--)
if(r<=0)
{
break;//如果最后一个数是第0个;退出循环
}
}
cout << ans << endl;//输出答案
return 0;
}

好吧我承认盗取了@CYC大佬的题解,但我这个小蒟蒻起码看懂了!

这使我更加有信心面对下一个题!

好的我们愉快的来到了第二题

ybt1202

好吧这个不是贪心题,但我觉得这个题含金量真的很高!

上题目!

 

【题目描述】

Pell数列a1,a2,a3,...a1,a2,a3,...的定义是这样的,a1=1,a2=2,...,an=2*an1+an2(n>2)

给出一个正整数 k,要求Pell数列的第 k 项模上 32767是多少。

【输入】

第1行是测试数据的组数 n,后面跟着 n行输入。每组测试数据占 1 行,包括一个正整数k(1k<1000000)

【输出】

n 行,每行输出对应一个输入。输出应是一个非负整数。

 

 

一开始,我定眼一看,这是一个无脑递归题。

写完了简单的代码,准备去吃中饭,一提交,满屏的TLE和答案错误,我意识到可能爆int了。

我只好坐下来重新改,既然数这么大,即使改成long long 也有爆掉的可能。但是看到%32767,我想起了我万年不用的每步取模。。。

我在递归计算的时候每一个都加了%32767这一句。正当我交上准备走人的时候。

还是满屏的TLE。

这不只是爆int的问题。

我们需要加快递归函数的计算!

怎么加快?

记忆化搜索万岁!!!

(还好这个记忆化好写,当时在新手村真的被那个记忆化的洛谷P1464卡死,真的是看题解都写不出来!)

上代码!

//1202
#include<iostream>
using namespace std;
int jiyihua[1000000];//记忆化数组
int a;
int Pell(int x)
{
if (jiyihua[x]!=0)
{
return jiyihua[x];//如果jiyihua[x]已经被搜过了,直接返回jiyihua[x]的值,不用进行下一步计算了,这样节省很多时间。(这里写一个记忆化,我这个小蒟蒻还是会一点的
}
if (x==1)
{
return 1;
}
if (x==2)
{
return 2;
}
return jiyihua[x]=(2*Pell(x-1)%32767+Pell(x-2)%32767)%32767;//每步取模,保证不爆数据范围,把结果存到记忆化数组里。
}//做了每步取模和记忆化后就是无脑递归了
int main()//下面很简单,就不说了
{
int n;
cin>>n;
for (int i=0;i<n;i++)
{
cin>>a;
cout<<Pell(a)<<endl;
}
return 0;
}

这个题看似简单,实际是一个深藏不漏的大坑!感谢出题人让我重温了记忆化和每步取模。(出题人mdzz,不能出一个无脑递归放松一下吗)

 

 

第三题,ybt1229

这第三个题就有点看似神犇,实则蒟蒻了,全然不及第二个。

这里还要感谢@SXY神犇和我一起快乐的刷题和讨论!

上题目!

【题目描述】

小S新买了一个掌上游戏机,这个游戏机由两节5号电池供电。为了保证能够长时间玩游戏,他买了很多5号电池,这些电池的生产商不同,质量也有差异,因而使用寿命也有所不同,有的能使用5个小时,有的可能就只能使用3个小时。显然如果他只有两个电池一个能用5小时一个能用3小时,那么他只能玩3个小时的游戏,有一个电池剩下的电量无法使用,但是如果他有更多的电池,就可以更加充分地利用它们,比如他有三个电池分别能用3、3、5小时,他可以先使用两节能用3个小时的电池,使用半个小时后再把其中一个换成能使用5个小时的电池,两个半小时后再把剩下的一节电池换成刚才换下的电池(那个电池还能用2.5个小时),这样总共就可以使用5.5个小时,没有一点浪费。

现在已知电池的数量和电池能够使用的时间,请你找一种方案使得使用时间尽可能的长。

【输入】

输入包含多组数据。每组数据包括两行,第一行是一个整数N(2≤N≤1000),表示电池的数目,接下来一行是N个正整数表示电池能使用的时间。

【输出】

对每组数据输出一行,表示电池能使用的时间,保留到小数点后1位。

 

我和@SXY一开始是懵逼的!!!

怎么搞?

陷入沉思......

首先,电池的数量不知道,其次,电池的使用时间不知道(不知道是1,还是3,还是4或是其他)

这怎么贪心!

五分钟后,我惊奇的发现,电池只有三小时和五小时两种长度!

然后@SXY发现只要不是两节电池一个长度3一个长度5,似乎都可以使用完,没有一点浪费!

我又发现除了一个 3小时一个 5小时,其他的答案都是电池总长度加起来除以二!

然后我们就开心的特判。。。

上代码!

//1229
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
int N;
int time=0;
double ans=0;
while (cin>>N)
{
ans=0;
for (int i=1;i<=N;i++)
{
cin>>time;
ans+=time;
}
if (N==2&&(ans==8)) printf("3.0");
else printf("%0.1lf\n",ans/2);
}
return 0;
}

 

没错代码就是这么短,只要不是一个3小时,一个5小时,就是加起来除以二!

这个大水题就这么被我们在欢笑声中解决了。。。

 

下面是我分享的最后一个题

ybt1225

废话不多说,上题目!

 

 

【题目描述】

某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属。但是他只带着一个口袋,口袋至多只能装重量为w的物品。

岛上金属有ss个种类, 每种金属重量不同,分别为n1,n2,...,ns,同时每个种类的金属总的价值也不同,分别为v1,v2,...,vs。KID想一次带走价值尽可能多的金属,问他最多能带走价值多少的金属。注意到金属是可以被任意分割的,并且金属的价值和其重量成正比。

【输入】

第1行是测试数据的组数k,后面跟着k组输入。

每组测试数据占3行。

第1行是一个正整数w(1w10000),表示口袋承重上限。

第2行是一个正整数s(1s100)s(1≤s≤100),表示金属种类。

第3行有2s个正整数,分别为n1,v1,n2,v2,...,ns,vs。v1,n2,v2,...,ns,vs分别为第一种,第二种,...,第s种金属的总重量和总价值(1ni10000,1vi10000)

【输出】

k行,每行输出对应一个输入。输出应精确到小数点后2位。

 

 

这其实是一个无脑贪心题,贪心策略很好判定。(因为有加红的那句话)

既然金属可以分割,那么我们可以轻易求出某种金属的性价比,然后用sort()排序,无脑拿性价比最高的就好了

既然有了贪心策略,代码就好写了。

上代码!(因为这道题我又被评测机卡了,所以会上两套代码)

(本蒟蒻的代码(0通过测试点)但是经多组数据测试和神犇题解给出的答案是一样的,不知道为什么过不了!求神犇大佬们找到我的BUG!!!谢谢!)

//1225
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
struct STU{
  double backpack;
  int kind;
  double weight;
  double value;
  double xingjiabi;
}QAQ[100];//定义一个结构体,用来存背包容量,金属的种类,重量,价值,性价比。并存到数组QAQ里。
bool cmp(STU a,STU b)
{
  return a.xingjiabi>b.xingjiabi;
}//定义cmp,等会sort()排序时用到,从高到低排序性价比
int main()
{
  double sum;//定义sum,用来求答案(总价值)
  cin>>n;//输入测试数据个数
  for (int i=0;i<n;i++)
  {
    sum=0;//清空sum;
    scanf("%lf%d",&QAQ[i].backpack,&QAQ[i].kind);//输入背包容量和总共金属种类
    for (int j=0;j<QAQ[i].kind;j++)
    {
       scanf("%lf%lf",&QAQ[j].weight,&QAQ[j].value);//输入第j种金属的重量和价值
       QAQ[j].xingjiabi=QAQ[j].value/QAQ[j].weight;//求出第j种金属的性价比
    }
    sort (QAQ,QAQ+QAQ[i].kind,cmp);//对所有金属进行快排
    for (int k=0;k<QAQ[i].kind;k++)//检索所有金属
    {
       if (QAQ[i].backpack>=QAQ[k].weight)

       //如果背包容量比第k种金属的总量还大,就把第k种金属全部拿走。(已经从大到小排好序,保证当前金属k的性价比最高)
       {
          sum=QAQ[k].value+sum;//把金属k的总价值累积到sum里
          QAQ[i].backpack=QAQ[i].backpack-QAQ[k].weight;//背包容量减金属k的重量
       }
       else//如果背包容量不如金属k的总量多,就尽可能多拿第k种金属(已经从大到小排好序,保证当前金属k的性价比最高)
       {
          sum=QAQ[i].backpack*QAQ[k].xingjiabi+sum;
          break;//因为背包容量用完啦,所以退出循环。
       }
    }
    printf("%0.2lf\n",sum);//输出此时的sum
  }
  return 0;//结束程序。
}

 

就当我信心满满的点了提交键后,我发现我被评测机狠狠的摆了一道。

再重复一下我这个小蒟蒻的请求

(请各位神犇大佬不吝赐教,我真的想知道为什么会爆啊啊啊啊啊啊!)

 

好了,不多BB了,下面上csdn一位神犇博主的题解!(侵权删)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define INF 999999999
#define N 101
using namespace std;
struct node{

int w;
int c;
double g;
}a[N],temp;
int main()
{
  int t;
  int W,m;
  cin>>t;
  while(t--)
  {
    double cnt=0;
    cin>>W>>m;
    for(int i=1;i<=m;i++)
    {
      cin>>a[i].w>>a[i].c;
      a[i].g=a[i].c*1.0/a[i].w;
    }
    for(int i=1;i<=m;i++)
      for(int j=i+1;j<=m;j++)
        if(a[i].g<a[j].g)
        {
          temp=a[i];
          a[i]=a[j];
          a[j]=temp;
        }
    for(int i=1;i<=m;i++)
    {
      if(W>=a[i].w)
      {
        cnt+=a[i].c;
        W-=a[i].w;
      }
      else
      {
        cnt+=a[i].g*W;
        break;
      }
    }
  printf("%.2lf\n",cnt);
  }
  return 0;
}

最后的最后,再次感谢和我一起刷题的大佬和小伙伴@曹宇琮,@宋旭远,我愿意和你们一起成长,一起快乐的当一个码农!

只要有你们陪我,我这个小蒟蒻在成为神犇的艰辛道路上不会寂寞和无助!!!

发表于

2020.2.18   23:54



posted @ 2020-02-18 23:54  ZTer  阅读(217)  评论(0编辑  收藏  举报