几个贪心和递归的练习题
刷贪心!刷递归!
今天一天刷了四个题,效率低下,不在状态。
洛谷P1090卡了我好长时间,也算一道隔夜题,昨天晚上就已经把大致的代码写好了,可是只能过一个点。
今天早上改了半个小时,没改出来。蒟蒻的我找到了神犇@CYC(感谢@CYC对我这个小蒟蒻的支持!)
先上题目!
题目描述
输入格式
第一行是一个整数 n(1≤n≤10000) ,表示果子的种类数。第二行包含 n 个整数,用空格分隔,第 i 个整数 a_i(1<= a_i<=20000) 是第 i 堆果子的数目。
输出格式
我没有过的具体原因是我没有把合并后得到的新果堆重新排序。(原来如此!!!)
改代码!
我把代码改成合并后重新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*an−1+an−2(n>2)
给出一个正整数 k,要求Pell数列的第 k 项模上 32767是多少。
【输入】
第1行是测试数据的组数 n,后面跟着 n行输入。每组测试数据占 1 行,包括一个正整数k(1≤k<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(1≤w≤10000),表示口袋承重上限。
第2行是一个正整数s(1≤s≤100)s(1≤s≤100),表示金属种类。
第3行有2s个正整数,分别为n1,v1,n2,v2,...,ns,vs。v1,n2,v2,...,ns,vs分别为第一种,第二种,...,第s种金属的总重量和总价值(1≤ni≤10000,1≤vi≤10000)。
【输出】
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